From fedf515165bfafaf4fb98252ecda1abe050c8da5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 18 Oct 2018 10:59:00 +1030 Subject: [PATCH] io: don't leave errno as a random value when we hit EOF. It's used inside io_finish; setting to 0 allows that to know we hit EOF on a read. Signed-off-by: Rusty Russell --- ccan/io/io.c | 12 +++++++++-- ccan/io/io.h | 6 ++++-- ccan/io/test/run-45-eof.c | 45 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 ccan/io/test/run-45-eof.c diff --git a/ccan/io/io.c b/ccan/io/io.c index c0dd9b83..bdcbf9e2 100644 --- a/ccan/io/io.c +++ b/ccan/io/io.c @@ -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; diff --git a/ccan/io/io.h b/ccan/io/io.h index c9ab228c..510ee0be 100644 --- a/ccan/io/io.h +++ b/ccan/io/io.h @@ -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 index 00000000..6a7f9921 --- /dev/null +++ b/ccan/io/test/run-45-eof.c @@ -0,0 +1,45 @@ +#include +/* Include the C files directly. */ +#include +#include +#include +#include +#include + +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(); +} -- 2.39.2