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. --