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-28 16:58:29
Also in: linux-fsdevel

On Mon, Jan 26, 2026, at 7:49 PM, Jeff Layton wrote:
On Mon, 2026-01-26 at 17:01 -0600, Trevor Gross wrote:
quoted
On Mon Jan 26, 2026 at 10:43 AM CST, Jeff Layton wrote:
quoted
On Mon, 2026-01-26 at 16:56 +0100, Jan Kara wrote:
quoted
On Mon 26-01-26 14:53:12, The 8472 wrote:
quoted
On 26/01/2026 13:15, Jan Kara wrote:
quoted
On Sun 25-01-26 10:37:01, Zack Weinberg wrote:
quoted
On Sat, Jan 24, 2026, at 4:57 PM, The 8472 wrote:
...
quoted
quoted
quoted
quoted
quoted
quoted
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.
So I've checked and in Linux ->flush callback for the file is called
whenever you close a file descriptor (regardless whether there are other
file descriptors pointing to the same file description) so it's upto
filesystem implementation what it decides to do and which error it will
return... Checking the implementations e.g. FUSE and NFS *will* return
delayed writeback errors on *first* descriptor close even if there are
other still open descriptors for the description AFAICS.
...and I really wish they _didn't_.

Reporting a writeback error on close is not particularly useful. Most
filesystems don't require you to write back all data on a close(). A
successful close() on those just means that no error has happened yet.

Any application that cares about writeback errors needs to fsync(),
full stop.
Is there a good middle ground solution here?
...
quoted
I was wondering if it could be worth a new fnctl that provides this kind
of "best effort" error checking behavior without having the strict
requirements of fsync. In effect, to report the errors that you might
currently get at close() before actually calling close() and losing the
fd.
...
A new fcntl(..., F_CHECKERR, ...) command that does a
file_check_and_advance_wb_err() on the fd and reports the result would
be pretty straightforward.

Would that be helpful for your use-case? This would be like a non-
blocking fsync that just reports whether an error has occurred since
the last F_CHECKERR or fsync().
I feel I need to point out that “should the kernel report errors on
close()” and “should the kernel add a new API to make life better for
programs that currently expect close() to report [some] errors” and
“should the Rust standard library propagate errors produced by close()
back up to the application” and “what should the close(2) manpage say
about errors” are four different conversation topics.

I am all in favor of moving toward a world where close() never fails
and there’s _something_ that reports write errors like fsync() without
also kicking your application off a performance cliff.  But that’s not
the world we live in today, and this thread started as a conversation
about revising the close(2) manpage, and I’d kinda like to *finish*
revising the manpage in, like, the next couple weeks, not several
years from now :-)  So I’d like to refocus on that topic.

Given what Jan Kara said earlier...
Checking the implementations e.g. FUSE and NFS *will* return delayed
writeback errors on *first* descriptor close even if there are other
still open descriptors for the description AFAICS.
...
fsync(2) must make sure data is persistently stored and return error if
it was not. Thus as a VFS person I'd consider it a filesystem bug if an
error preveting reading data later was not returned from fsync(2). OTOH
that doesn't necessarily mean that later close doesn't return an error -
e.g. FUSE does communicate with the server on close that can fail and
error can be returned.

With this in mind let me now try to answer your remaining questions:
quoted
quoted
quoted
        - The OFD was opened with O_RDONLY
If the filesystem supports atime, close can in principle report that atime
update failed.
quoted
quoted
quoted
        - The OFD was opened with O_RDWR but has never actually
          been written to
The same as above but with inode mtime updates.
quoted
quoted
quoted
        - No data has been written to the OFD since the last call to
          fsync() for that OFD
No writeback errors should happen in this case. As I wrote above I'd
consider this a filesystem bug.
quoted
quoted
quoted
        - No data has been written to the OFD since the last call to
          fdatasync() for that OFD
Errors can happen because some inode metadata (in practice probably only
inode time stamps) may still need to be written out.

So in the cases described above (except for fsync()) you may get delayed
errors on close. But since in all those cases no data is lost, I don't
think 99.9% of applications care at all...
... regrettably I think this does mean the close(3) manpage still needs
to tell people to watch out for errors, and should probably say that
errors _can_ happen even if the file wasn’t written to, but are much
less likely to be important in that case.

And my “how to close stdout in a thread-safe manner” sample code is
wrong, because I was wrong to think that the error reporting only
happened on the _final_ close, when the OFD is destroyed.

... What happens if the close is implicit in a dup2() operation? Here’s
that erroneous “how to close stdout” fragment, with comments
indicating what I thought could and could not fail at the time I wrote
it:

    // These allocate new fds, which can always fail, e.g. because
    // the program already has too many files open.
    int new_stdout = open("/dev/null", O_WRONLY);
    if (new_stdout == -1) perror_exit("/dev/null");
    int old_stdout = dup(1);
    if (old_stdout == -1) perror_exit("dup(1)");

    flockfile(stdout);
    if (fflush(stdout)) perror_exit("stdout: write error");
    dup2(new_stdout, 1); // cannot fail, atomically replaces fd 1
    funlockfile(stdout);

    // this close may receive delayed write errors from previous writes
    // to stdout
    if (close(old_stdout)) perror_exit("stdout: write error");

    // this close cannot fail, because it only drops an alternative
    // reference to the open file description now installed as fd 1
    close(new_stdout);

Note in particular that the first close _operation_ on fd 1 is in
consequence of dup2(new_stdout, 1).  The dup2() manpage specifically
says “the close is performed silently (i.e. any errors during the
close are not reported by dup()” but, if stdout points to a file on
an NFS mount, are those errors _lost_, or will they actually be
reported by the subsequent close(old_stdout)?

Incidentally, the dup2() manpage has a very similar example in its
NOTES section, also presuming that close only reports errors on the
_final_ close, not when it “merely” drops reference >=2 to an OFD.

(I’m starting to think we need dup3(old, new, O_SWAP_FDS).  Or is that
already a thing somehow?)

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