[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