Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can write(2) return 0 bytes written*, and what to do if it does?

Tags:

I'd like to implement a proper write(2) loop that takes a buffer and keeps calling write until the entire buffer is written.

I guess the basic approach is something like:

/** write len bytes of buf to fd, returns 0 on success */ int write_fully(int fd, char *buf, size_t len) {   while (len > 0) {     ssize_t written = write(fd, buf, len);     if (written < 0) {       // some kind of error, probably should try again if its EINTR?       return written;     }     buf += written;     len -= written;   }   return 0; }  

... but this raises the question of whether write() can validly return 0 bytes written and what to do in this case. If the situation persists, the above code will just hot spin on the write call which seems like a bad idea. As long as something other than zero is returned you are making forward progress.

The man page for write is a bit ambiguous. It says, for example:

On success, the number of bytes written is returned (zero indicates nothing was written).

Which seems to indicate that it is possible in some scenarios. Only one such scenario is explicitly called out:

If count is zero and fd refers to a regular file, then write() may return a failure status if one of the errors below is detected. If no errors are detected, or error detection is not performed, 0 will be returned without causing any other effect. If count is zero and fd refers to a file other than a regular file, the results are not specified.

That case is avoided above because I never call write with len == 0. There are lot of other cases where nothing could be written, but in general they all have specific error codes associated with them.

The file itself will be a opened from a path/name given on the command line. So it will usually be a regular file, but users may of course pass things like pipes, do input redirection, pass special devices like /dev/stdout and so on. I am at least in control of the open call and the O_NONBLOCK flag is not passed to open. I can't reasonably check the behavior for all the file systems, all the special devices (and even if I could, more will be added), so I want to know how to handle this in a reasonable and general way.


* ... for a non-zero buffer size.

like image 285
BeeOnRope Avatar asked Jan 27 '17 22:01

BeeOnRope


People also ask

What does write () do in C?

The write() function shall attempt to write nbyte bytes from the buffer pointed to by buf to the file associated with the open file descriptor, fildes. Before any action described below is taken, and if nbyte is zero and the file is a regular file, the write() function may detect and return errors as described below.

What does the write () system call return on failure?

It is a signed data type defined in stddef. h . Note that write() does not return an unsigned value; it returns -1 if an error occurs so it must return a signed value. The write function returns the number of bytes successfully written into the file, which may at times be less than the specified nbytes.

What does read () in C do?

The read() function reads data previously written to a file. If any portion of a regular file prior to the end-of-file has not been written, read() shall return bytes with value 0. For example, lseek() allows the file offset to be set beyond the end of existing data in the file.

What is socket write return?

Returned Value If successful, write() returns the number of bytes actually written, less than or equal to N . If unsuccessful, it returns the value -1 and sets errno to one of the following: A value of 0 or greater indicates the number of bytes sent. However, this does not assure that data delivery was complete.


2 Answers

TL;DR summary

Unless you go out of your way to invoke unspecified behaviour, you will not get a zero result back from write() unless, perhaps, you attempt to write zero bytes (which the code in the question avoids doing).

POSIX says:

The POSIX specification for write() covers the issue, I believe.

The write() function shall attempt to write nbyte bytes from the buffer pointed to by buf to the file associated with the open file descriptor, fildes.

Before any action described below is taken, and if nbyte is zero and the file is a regular file, the write() function may detect and return errors as described below. In the absence of errors, or if error detection is not performed, the write() function shall return zero and have no other results. If nbyte is zero and the file is not a regular file, the results are unspecified.

This states that if you request a write of zero bytes, you may get a return value of zero, but there are a bundle of caveats — it must be a regular file, and you might get an error if errors like EBADF are detected, and it is unspecified what happens if the file descriptor does not refer to a regular file.

If a write() requests that more bytes be written than there is room for (for example, [XSI]⌦ the file size limit of the process or ⌫ the physical end of a medium), only as many bytes as there is room for shall be written. For example, suppose there is space for 20 bytes more in a file before reaching a limit. A write of 512 bytes will return 20. The next write of a non-zero number of bytes would give a failure return (except as noted below).

[XSI]⌦ If the request would cause the file size to exceed the soft file size limit for the process and there is no room for any bytes to be written, the request shall fail and the implementation shall generate the SIGXFSZ signal for the thread. ⌫

If write() is interrupted by a signal before it writes any data, it shall return -1 with errno set to [EINTR].

If write() is interrupted by a signal after it successfully writes some data, it shall return the number of bytes written.

If the value of nbyte is greater than {SSIZE_MAX}, the result is implementation-defined.

These rules do not really give permission to return 0 (though a pedant might say that a value of nbyte that's too large might be defined to return 0).

When attempting to write to a file descriptor (other than a pipe or FIFO) that supports non-blocking writes and cannot accept the data immediately:

  • If the O_NONBLOCK flag is clear, write() shall block the calling thread until the data can be accepted.

  • If the O_NONBLOCK flag is set, write() shall not block the thread. If some data can be written without blocking the thread, write() shall write what it can and return the number of bytes written. Otherwise, it shall return -1 and set errno to [EAGAIN].

…details for obscure file types — a number of them with unspecified behaviour…

Return value

Upon successful completion, these functions shall return the number of bytes actually written to the file associated with fildes. This number shall never be greater than byte. Otherwise, -1 shall be returned and errno set to indicate the error.

So, since your code avoids attempting to write zero bytes, as long as len is not larger than {SSIZE_MAX}, and as long as you aren't writing to obscure file types (like a shared memory object or a typed memory object) you should not see zero returned by write().


POSIX Rationale says:

Later in the POSIX page for write(), in the Rationale section, there is the information:

Where this volume of POSIX.1-2008 requires -1 to be returned and errno set to [EAGAIN], most historical implementations return zero (with the O_NDELAY flag set, which is the historical predecessor of O_NONBLOCK, but is not itself in this volume of POSIX.1-2008). The error indications in this volume of POSIX.1-2008 were chosen so that an application can distinguish these cases from end-of-file. While write() cannot receive an indication of end-of-file, read() can, and the two functions have similar return values. Also, some existing systems (for example, Eighth Edition) permit a write of zero bytes to mean that the reader should get an end-of-file indication; for those systems, a return value of zero from write() indicates a successful write of an end-of-file indication.

Thus, although POSIX (largely if not wholly) precludes the possibility of a zero return from write(), there was prior art on related systems that did have write() return zero.

like image 96
Jonathan Leffler Avatar answered Sep 19 '22 09:09

Jonathan Leffler


It depends on what the file descriptor refers to. When you call write on a file descriptor, the kernel ultimately ends up calling the write routine in the associated file operations vector, which corresponds to the underlying file system or device that the file descriptor refers to.

Most normal file systems will never return 0, but devices might do just about anything. You need to look at the documentation for the device in question to see what it might do. It is legal for a device driver to return 0 bytes written (the kernel won't flag it as an error or anything), and if it does, the write system call will return 0.

like image 44
Chris Dodd Avatar answered Sep 19 '22 09:09

Chris Dodd