[Bug 296936] [fusefs] write() to a file opened with O_APPEND fails with EBADF
[email protected] Mon, 20 Jul 2026 19:58:54 +0000
| Newsgroups | gmane.os.freebsd.bugs |
|---|---|
| Message-ID | <[email protected]/bugzilla/> |
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=296936
Bug ID: 296936
Summary: [fusefs] write() to a file opened with O_APPEND fails
with EBADF
Product: Base System
Version: 15.1-STABLE
Hardware: Any
OS: Any
Status: New
Severity: Affects Some People
Priority: ---
Component: kern
Assignee: [email protected]
Reporter: [email protected]
Description
***********
On a fusefs mount, a file opened with O_APPEND can be opened successfully, but
the first write to that descriptor fails with errno 9 (EBADF) and the data is
lost.
A plain O_WRONLY (create/truncate) write to the same file works normally.
The failure seems to be in the kernel fusefs driver and is independent of both
the FUSE daemon and the underlying storage: it reproduces with any passthrough
daemon over any local filesystem (ZFS, tmpfs, p9fs, ...).
It breaks every append-mode workflow: shell >>, tee -a, log writers, editors
that append, etc.
Testing Environment
*******************
FreeBSD 15.1-RELEASE-p1 GENERIC arm64
fusefs-libs3-3.18.1
sysutils/fusefs-bindfs-1.18.3 (used only as a trivial passthrough daemon)
Steps to reproduce (as root; bindfs is just a minimal passthrough)
******************************************************************
pkg install -y fusefs-bindfs
kldload fusefs
mkdir -p /tmp/lower /tmp/upper
bindfs /tmp/lower /tmp/upper
printf 'first\n' > /tmp/upper/f # O_WRONLY|O_CREAT|O_TRUNC -> OK
printf 'second\n' >> /tmp/upper/f # O_WRONLY|O_APPEND -> fails
truss of the append shows the open succeeding and the write failing:
openat(AT_FDCWD,"/tmp/upper/f",O_WRONLY|O_APPEND|O_CREAT,0666) = 3 (0x3)
write(3,"second\n",7) ERR#9 'Bad file descriptor'
Expected result
***************
The append succeeds and f contains:
first
second
Actual result
*************
write returns EBADF; the appended data is discarded. f still contains only
first.
Analysis
********
The append is serviced through the buffered (bio) write path.
In sys/fs/fuse/fuse_io.c, fuse_io_strategy() resolves the write file handle
with a pid of 0:
/* We don't know the true pid when we're dealing with the cache */
pid_t pid = 0;
...
error = fuse_filehandle_getrw(vp, fflag, &fufh, cred, pid);
if (bp->b_iocmd == BIO_READ && error == EBADF) {
/* read-modify-write on a cached O_WRONLY file: retry */
error = fuse_filehandle_get(vp, FWRITE, &fufh, cred, pid);
}
if (error) {
printf("FUSE: strategy: filehandles are closed\n");
bp->b_ioflags |= BIO_ERROR;
...
}
For a BIO_WRITE there is no fallback equivalent to the BIO_READ retry, so when
fuse_filehandle_getrw() cannot match a write handle (here because of pid == 0)
the strategy routine sets BIO_ERROR and EBADF propagates to userland.
Routing the append through the direct path (fuse_write_directbackend(), which
handles IO_APPEND via uio_setoffset(uio, filesize)) avoids the problem, but
there is currently no way to force it: per-filehandle direct_io is broken (bug
#293088), the -o direct_io mount option is rejected by libfuse, and
vfs.fusefs.data_cache_mode = 0, 1, and 2 all reproduce the failure.
Notes
*****
- Reproduces regardless of the lower filesystem (verified on ZFS, tmpfs, and
p9fs).
- vfs.fusefs.data_cache_mode = 0 / 1 / 2 all fail identically.
- -o default_permissions does not change the behavior.
- Likely the same root cause as bug #236379 (cached write path lacks the true
pid); a working per-fd direct_io path (bug #293088) would also avoid it.
- I have discovered this bug while using FreeBSD + fusefs-bindfs
- This bug report was co-written with the aid of "Claude Opus 4.8"
--
You are receiving this mail because:
You are the assignee for the bug.