Thread (49 messages) flat view 49 messages, 14 authors, 2026-02-06

Re: [RFC v1] man/man2/close.2: CAVEATS: Document divergence from POSIX.1-2024

From: Zack Weinberg <hidden>
Date: 2026-01-25 15:37:43
Also in: linux-fsdevel

On Sat, Jan 24, 2026, at 4:57 PM, The 8472 wrote:
On 24/01/2026 22:39, Rich Felker wrote:
quoted
On Sat, Jan 24, 2026 at 08:34:01PM +0100, The 8472 wrote:
quoted
On 23/01/2026 01:33, Zack Weinberg wrote:

[...]
quoted
ERRORS
         EBADF  The fd argument was not a valid, open file descriptor.
Unfortunately EBADF from FUSE is passed through unfiltered by the kernel
on close[0], that makes it more difficult to reliably detect bugs relating
to double-closes of file descriptors.
Wow, that's a nasty bug. Are the kernel folks not amenable to fixing
it?
Not when I brought it up last time, no[0]

[0] https://lore.kernel.org/linux-fsdevel/1b946a20-5e8a-497e-96ef-f7b1e037edcb@infinite-source.de/ (local)
It seems to me that Antonio Muscemi’s point is valid for *most* errno
codes.  Like, a whole lot of them exist just to give more information
*to a human user* about the cause of an unrecoverable error.  Take
the list of “error codes that indicate a delayed error from a previous
write(2) operation,” from a little later in the draft, for instance:
there’s no plausible way for a *program* to react differently to
EFBIG, EDQUOT, and ENOSPC, but we expect that the *user* will want
to react differently, so we want different error messages for each,
so they’re different error codes.  It’s not a problem if the kernel
produces an error code of this type that wasn’t in the official
documented list, because the program doesn’t need to treat it specially.

But EBADF is different; it has the very specific meaning “user space
passed an invalid file descriptor to a system call,” which almost
always indicates a *bug in the program*, and allowing that meaning to
be diluted is not OK.  It’s getting off topic for this conversation,
but there’s a short list of other errno codes that indicate a specific
situation that the *program* should respond to in a specific way
(EAGAIN, EINTR, EINPROGRESS, EFAULT, and EPIPE are the only ones
I can think of) and maybe it would spark a more constructive
conversation on the kernel side if we presented a *comprehensive*
list of errno codes that FUSE servers shouldn’t be allowed to produce
with a specific rationale for each.
quoted
    Delayed errors reported by close()

        In a variety of situations, most notably when writing to a file
        that is hosted on a network file server, write(2) operations may
        “optimistically” return successfully as soon as the write has
        been queued for processing.

        close(2) waits for confirmation that *most* of the processing
        for previous writes to a file has been completed, and reports
        any errors that the earlier write() calls *would have* reported,
        if they hadn’t returned optimistically.  Especially, close()
        will report “disk full” (ENOSPC) and “disk quota exceeded”
        (EDQUOT) errors that write() didn’t wait for.
The Rust standard library team is also interested in this topic, there
is lively discussion[1] whether it makes sense to surface errors from
close at all. Our current default is to ignore them.
It is my understanding that errors may not have happened yet at
the time of close due to delayed writeback or additional descriptors
pointing to the description, e.g. in a forked child, and thus
close() is not a reliable mechanism for error detection and
fsync() is the only available option.

[1] https://github.com/rust-lang/libs-team/issues/705
This is something I care about a lot as well, but I currently don’t
have an *opinion*.  To form an informed opinion, I need the answers
to these questions:
quoted
     [QUERY: Do delayed errors ever happen in any of these situations?

        - The fd is not the last reference to the open file description

        - The OFD was opened with O_RDONLY

        - The OFD was opened with O_RDWR but has never actually
          been written to

        - No data has been written to the OFD since the last call to
          fsync() for that OFD

        - No data has been written to the OFD since the last call to
          fdatasync() for that OFD

        If we can give some guidance about when people don’t need to
        worry about delayed errors, it would be helpful.]
In particular, I really hope delayed errors *aren’t* ever reported
when you close a file descriptor that *isn’t* the last reference
to its open file description, because the thread-safe way to close
stdout without losing write errors[2] depends on that not happening.

And whether the Rust stdlib can legitimately say “leaving aside the
additional cost of calling fsync(), you do not *need* the error return
from close() because you can call fsync() first,” depends on whether
it’s actually true that you *won’t* ever get a delayed error from
close() if you called fsync() first and didn’t do any more output in
between (assume the fd has no duplicates here).  I would not be
surprised at all if those FUSE guys insisted on their right to make

    char msg[] = "soon I will be invincible\n";
    int fd = open("/test-fuse-fs/test.txt", O_WRONLY, 0666);
    write(fd, msg, sizeof(msg) - 1);
    fsync(fd);
    close(fd);

return an error *only* from the close, not the write or the fsync.
And I also wouldn’t be surprised at all to find production NFS or
SMB servers that did that.

[2] https://stackoverflow.com/a/50865617 (third code block)

zw
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help