io: don't leave errno as a random value when we hit EOF.
authorRusty Russell <rusty@rustcorp.com.au>
Thu, 18 Oct 2018 00:29:00 +0000 (10:59 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Thu, 18 Oct 2018 00:29:00 +0000 (10:59 +1030)
It's used inside io_finish; setting to 0 allows that to know we hit
EOF on a read.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ccan/io/io.c
ccan/io/io.h
ccan/io/test/run-45-eof.c [new file with mode: 0644]

index c0dd9b838e84af6ec8ef6b62dec8fe3695adf94c..bdcbf9e26b8f9f580fea08c2c0913d31efe9dcc7 100644 (file)
@@ -202,8 +202,12 @@ struct io_plan *io_write_(struct io_conn *conn, const void *data, size_t len,
 static int do_read(int fd, struct io_plan_arg *arg)
 {
        ssize_t ret = read(fd, arg->u1.cp, arg->u2.s);
-       if (ret <= 0)
+       if (ret <= 0) {
+               /* Errno isn't set if we hit EOF, so set it to distinct value */
+               if (ret == 0)
+                       errno = 0;
                return -1;
+       }
 
        arg->u1.cp += ret;
        arg->u2.s -= ret;
@@ -230,8 +234,12 @@ struct io_plan *io_read_(struct io_conn *conn,
 static int do_read_partial(int fd, struct io_plan_arg *arg)
 {
        ssize_t ret = read(fd, arg->u1.cp, *(size_t *)arg->u2.vp);
-       if (ret <= 0)
+       if (ret <= 0) {
+               /* Errno isn't set if we hit EOF, so set it to distinct value */
+               if (ret == 0)
+                       errno = 0;
                return -1;
+       }
 
        *(size_t *)arg->u2.vp = ret;
        return 1;
index c9ab228c7a046b3c3575d0c1aa40d1255b15f5d7..510ee0be0166033618768b91cbcafcec8541c85b 100644 (file)
@@ -223,7 +223,8 @@ struct io_plan *io_write_(struct io_conn *conn,
  *
  * This creates a plan to read data into a buffer.  Once it's all
  * read, the @next function will be called: on an error, the finish
- * function is called instead.
+ * function is called instead.  If read() returns 0 (EOF) errno is set
+ * to 0.
  *
  * Note that the I/O may actually be done immediately.
  *
@@ -256,7 +257,8 @@ struct io_plan *io_read_(struct io_conn *conn,
  *
  * This creates a plan to read data into a buffer.  Once any data is
  * read, @len is updated and the @next function will be called: on an
- * error, the finish function is called instead.
+ * error, the finish function is called instead.  If read() returns 0 (EOF)
+ * errno is set to 0.
  *
  * Note that the I/O may actually be done immediately.
  *
diff --git a/ccan/io/test/run-45-eof.c b/ccan/io/test/run-45-eof.c
new file mode 100644 (file)
index 0000000..6a7f992
--- /dev/null
@@ -0,0 +1,45 @@
+#include <ccan/io/io.h>
+/* Include the C files directly. */
+#include <ccan/io/poll.c>
+#include <ccan/io/io.c>
+#include <ccan/tap/tap.h>
+#include <sys/wait.h>
+#include <stdio.h>
+
+static size_t len;
+
+static void finished_read(struct io_conn *conn, int *expect)
+{
+       ok1(errno == *expect);
+}
+
+static struct io_plan *init_conn_read(struct io_conn *conn, int *expect)
+{
+       io_set_finish(conn, finished_read, expect);
+       return io_read(conn, &expect, sizeof(expect), io_never, expect);
+}
+
+static struct io_plan *init_conn_read_partial(struct io_conn *conn, int *expect)
+{
+       io_set_finish(conn, finished_read, expect);
+       return io_read_partial(conn, &expect, sizeof(expect), &len,
+                              io_never, expect);
+}
+
+int main(void)
+{
+       int fd, expect_errno = 0;
+
+       /* This is how many tests you plan to run */
+       plan_tests(2);
+       fd = open("/dev/null", O_RDONLY);
+       io_new_conn(NULL, fd, init_conn_read, &expect_errno);
+
+       fd = open("/dev/null", O_RDONLY);
+       io_new_conn(NULL, fd, init_conn_read_partial, &expect_errno);
+
+       io_loop(NULL, NULL);
+
+       /* This exits depending on whether all tests passed */
+       return exit_status();
+}