fd: Let write_all() return an error on short writes.
authorAndre Noll <maan@systemlinux.org>
Sun, 11 Dec 2011 20:12:13 +0000 (21:12 +0100)
committerAndre Noll <maan@systemlinux.org>
Fri, 20 Jan 2012 21:57:07 +0000 (22:57 +0100)
Short writes are often ignored because most callers of write_all()
do not check the return value. Rather than fixing all callers, this
patch teaches write_all() to detect short writes and return the new
E_SHORT_WRITE error code in this case.

Currently write_all() is a wrapper for write(), similar to
write_nonblock() but lacking the safety checks of the latter. To get
these safety checks, write_all() is changed to call write_nonblock().

Unfortunately, this involves some code movement.

error.h
fd.c

diff --git a/error.h b/error.h
index 63d4d5d..f7216ea 100644 (file)
--- a/error.h
+++ b/error.h
@@ -441,6 +441,7 @@ extern const char **para_errlist[];
        PARA_ERROR(FGETS, "fgets error"), \
        PARA_ERROR(EOF, "end of file"), \
        PARA_ERROR(READ_PATTERN, "did not read expected pattern"), \
+       PARA_ERROR(SHORT_WRITE, "unexpected short write"), \
 
 
 #define ALSA_WRITE_ERRORS \
diff --git a/fd.c b/fd.c
index 830b15d..01c7ed8 100644 (file)
--- a/fd.c
+++ b/fd.c
 #include "string.h"
 #include "fd.h"
 
-/**
- * Write a buffer to a file descriptor, re-write on short writes.
- *
- * \param fd The file descriptor.
- * \param buf The buffer to be sent.
- * \param len The length of \a buf.
- *
- * \return Standard.
- */
-int write_all(int fd, const char *buf, size_t len)
-{
-       size_t total = len;
-
-       assert(total);
-       len = 0;
-       while (len < total) {
-               int ret = write(fd, buf + len, total - len);
-               if (ret == -1)
-                       return -ERRNO_TO_PARA_ERROR(errno);
-               len += ret;
-       }
-       return len;
-}
-
-/**
- * Send a buffer given by a format string.
- *
- * \param fd The file descriptor.
- * \param fmt A format string.
- *
- * \return Standard.
- */
-__printf_2_3 int write_va_buffer(int fd, const char *fmt, ...)
-{
-       char *msg;
-       int ret;
-
-       PARA_VSPRINTF(fmt, msg);
-       ret = write_buffer(fd, msg);
-       free(msg);
-       return ret;
-}
-
 /**
  * Write a buffer to a file descriptor, re-writing on short writes.
  *
@@ -109,6 +66,48 @@ int write_nonblock(int fd, const char *buf, size_t len)
        return written;
 }
 
+/**
+ * Write all data to a file descriptor.
+ *
+ * \param fd The file descriptor.
+ * \param buf The buffer to be sent.
+ * \param len The length of \a buf.
+ *
+ * This is like \ref write_nonblock() but returns \p -E_SHORT_WRITE if not
+ * all data could be written.
+ *
+ * \return Number of bytes written on success, negative error code else.
+ */
+int write_all(int fd, const char *buf, size_t len)
+{
+       int ret = write_nonblock(fd, buf, len);
+
+       if (ret < 0)
+               return ret;
+       if (ret != len)
+               return -E_SHORT_WRITE;
+       return ret;
+}
+
+/**
+ * Write a buffer given by a format string.
+ *
+ * \param fd The file descriptor.
+ * \param fmt A format string.
+ *
+ * \return The return value of the underlying call to \ref write_all().
+ */
+__printf_2_3 int write_va_buffer(int fd, const char *fmt, ...)
+{
+       char *msg;
+       int ret;
+
+       PARA_VSPRINTF(fmt, msg);
+       ret = write_buffer(fd, msg);
+       free(msg);
+       return ret;
+}
+
 /**
  * Read from a non-blocking file descriptor into multiple buffers.
  *