While nbd_internal_fork_safe_perror() must indeed call write(), and
arguably justifiedly ignores the return value of write(), we can still
make the write operations slightly more robust. Let's do that by
introducing xwrite():
- don't call write() with nbyte > SSIZE_MAX, for avoiding
implementation-defined behavior,
- handle partial writes,
- cope with EINTR and EAGAIN errors. (A busy loop around EAGAIN on a
non-blocking file is not great in the general case, but it's good enough
for writing out diagnostics before giving up.)
Signed-off-by: Laszlo Ersek <lersek(a)redhat.com>
---
lib/utils.c | 39 ++++++++++++++++++--
1 file changed, 35 insertions(+), 4 deletions(-)
diff --git a/lib/utils.c b/lib/utils.c
index bba4b3846e77..7fb16f6402d1 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -25,6 +25,7 @@
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
#include "minmax.h"
@@ -181,6 +182,36 @@ nbd_internal_fork_safe_itoa (long v, char *buf, size_t bufsize)
#pragma GCC diagnostic ignored "-Wunused-result"
#endif
+/* "Best effort" function for writing out a buffer to a file descriptor.
+ * Chunking with SSIZE_MAX (for avoiding implementation-defined behavior),
+ * partial writes, and EINTR and EAGAIN failures are handled internally. No
+ * value is returned; only use this for writing diagnostic data on error paths,
+ * when giving up on a higher-level action anyway. Note that this function is
+ * supposed to remain async-signal-safe.
+ */
+static void
+xwrite (int fd, const void *buf, size_t count)
+{
+ const unsigned char *pos;
+ size_t left;
+
+ pos = buf;
+ left = count;
+ while (left > 0) {
+ ssize_t written;
+
+ do
+ written = write (fd, pos, MIN (left, SSIZE_MAX));
+ while (written == -1 && (errno == EINTR || errno == EAGAIN));
+
+ if (written == -1)
+ return;
+
+ pos += written;
+ left -= written;
+ };
+}
+
/* Fork-safe version of perror. ONLY use this after fork and before
* exec, the rest of the time use set_error().
*/
@@ -191,8 +222,8 @@ nbd_internal_fork_safe_perror (const char *s)
const char *m = NULL;
char buf[32];
- write (2, s, strlen (s));
- write (2, ": ", 2);
+ xwrite (2, s, strlen (s));
+ xwrite (2, ": ", 2);
#ifdef HAVE_STRERRORDESC_NP
m = strerrordesc_np (errno);
#else
@@ -202,8 +233,8 @@ nbd_internal_fork_safe_perror (const char *s)
#endif
if (!m)
m = nbd_internal_fork_safe_itoa ((long) errno, buf, sizeof buf);
- write (2, m, strlen (m));
- write (2, "\n", 1);
+ xwrite (2, m, strlen (m));
+ xwrite (2, "\n", 1);
/* Restore original errno in case it was disturbed by the system
* calls above.