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

Zack Weinberg zack@owlfolio.org
Tue Jan 20 16:15:15 GMT 2026


Rich and I have an irreconciliable disagreement on what the semantics of close
_should_ be.  I'm not going to do any more work on this until/unless he
changes his mind.

On Sun, Jan 18, 2026, at 5:23 PM, Alejandro Colomar wrote:
> Hi Zack and others,
>
> Just a gentle ping.  It would be nice to have an agreement for some
> patch.
>
>
> Have a lovely night!
> Alex
>
> On Fri, May 23, 2025 at 02:10:57PM -0400, Zack Weinberg wrote:
>> Taking everything said in this thread into account, I have attempted to
>> wordsmith new language for the close(2) manpage.  Please let me know
>> what you think, and please help me with the bits marked in square
>> brackets. I can make this into a proper patch for the manpages
>> when everyone is happy with it.
>> 
>> zw
>> 
>> ---
>> 
>> DESCRIPTION
>>     ... existing text ...
>> 
>>     close() always succeeds.  That is, after it returns, _fd_ has
>>     always been disconnected from the open file it formerly referred
>>     to, and its number can be recycled to refer to some other file.
>>     Furthermore, if _fd_ was the last reference to the underlying
>>     open file description, the resources associated with the open file
>>     description will always have been scheduled to be released.
>> 
>>     However, close may report _delayed errors_ from a previous I/O
>>     operation.  Therefore, its return value should not be ignored.
>> 
>> RETURN VALUE
>>     close() returns zero if there are no delayed errors to report,
>>     or -1 if there _might_ be delayed errors.
>> 
>>     When close() returns -1, check _errno_ to see what the situation
>>     actually is.  Most, but not all, _errno_ codes indicate a delayed
>>     I/O error that should be reported to the user.  See ERRORS and
>>     NOTES for more detail.
>> 
>>     [QUERY: Is it ever possible to get delayed errors on close() from
>>     a file that was opened with O_RDONLY?  What about a file that was
>>     opened with O_RDWR but never actually written to?  If people only
>>     have to worry about delayed errors if the file was actually
>>     written to, we should say so at this point.
>> 
>>     It would also be good to mention whether it is possible to get a
>>     delayed error on close() even if a previous call to fsync() or
>>     fdatasync() succeeded and there haven’t been any more writes to
>>     that file *description* (not necessarily via the fd being closed)
>>     since.]
>> 
>> ERRORS
>>     EBADF  _fd_ wasn’t open in the first place, or is outside the
>>            valid numeric range for file descriptors.
>> 
>>     EINPROGRESS
>>     EINTR
>>            There are no delayed errors to report, but the kernel is
>>            still doing some clean-up work in the background.  This
>>            situation should be treated the same as if close() had
>>            returned zero.  Do not retry the close(), and do not report
>>            an error to the user.
>> 
>>     EDQUOT
>>     EFBIG
>>     EIO
>>     ENOSPC
>>            These are the most common errno codes associated with
>>            delayed I/O errors.  They should be treated as a hard
>>            failure to write to the file that was formerly associated
>>            with _fd_, the same as if an earlier write(2) had failed
>>            with one of these codes.  The file has still been closed!
>>            Do not retry the close().  But do report an error to the user.
>> 
>>     Depending on the underlying file, close() may return other errno
>>     codes; these should generally also be treated as delayed I/O errors.
>> 
>> NOTES
>>   Dealing with error returns from close()
>> 
>>     As discussed above, close() always closes the file.  Except when
>>     errno is set to EBADF, EINPROGRESS, or EINTR, an error return from
>>     close() reports a _delayed I/O error_ from a previous write()
>>     operation.
>> 
>>     It is vital to report delayed I/O errors to the user; failing to
>>     check the return value of close() can cause _silent_ loss of data.
>>     The most common situations where this actually happens involve
>>     networked filesystems, where, in the name of throughput, write()
>>     often returns success before the server has actually confirmed a
>>     successful write.
>> 
>>     However, it is also vital to understand that _no matter what_
>>     close() returns, and _no matter what_ it sets errno to, when it
>>     returns, _the file descriptor passed to close() has been closed_,
>>     and its number is _immediately_ available for reuse by open(2),
>>     dup(2), etc.  Therefore, one should never retry a close(), not
>>     even if it set errno to a value that normally indicates the
>>     operation needs to be retried (e.g. EINTR).  Retrying a close()
>>     is a serious bug, particularly in a multithreaded program; if
>>     the file descriptor number has already been reused, _that file_
>>     will get closed out from under whatever other thread opened it.
>> 
>>     [Possibly something about fsync/fdatasync here?]
>> 
>> BUGS
>>     Prior to POSIX.1-2024, there was no official guarantee that
>>     close() would always close the file descriptor, even on error.
>>     Linux has always closed the file descriptor, even on error,
>>     but other implementations might not have.
>> 
>>     The only such implementation we have heard of is HP-UX; at least
>>     some versions of HP-UX’s man page for close() said it should be
>>     retried if it returned -1 with errno set to EINTR.  (If you know
>>     exactly which versions of HP-UX are affected, or of any other
>>     Unix where close() doesn’t always close the file descriptor,
>>     please contact us about it.)
>> 
>>     Portable code should nonetheless never retry a failed close(); the
>>     consequences of a file descriptor leak are far less dangerous than
>>     the consequences of closing a file out from under another thread.
>
> -- 
> <https://www.alejandro-colomar.es>
>
> Attachments:
> * signature.asc


More information about the Libc-alpha mailing list