diff --git a/include/io/channel.h b/include/io/channel.h index 8f25893c45..3995e243a3 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -268,6 +268,36 @@ ssize_t qio_channel_writev_full(QIOChannel *ioc, size_t nfds, Error **errp); +/** + * qio_channel_readv_all_eof: + * @ioc: the channel object + * @iov: the array of memory regions to read data into + * @niov: the length of the @iov array + * @errp: pointer to a NULL-initialized error object + * + * Read data from the IO channel, storing it in the + * memory regions referenced by @iov. Each element + * in the @iov will be fully populated with data + * before the next one is used. The @niov parameter + * specifies the total number of elements in @iov. + * + * The function will wait for all requested data + * to be read, yielding from the current coroutine + * if required. + * + * If end-of-file occurs before any data is read, + * no error is reported; otherwise, if it occurs + * before all requested data has been read, an error + * will be reported. + * + * Returns: 1 if all bytes were read, 0 if end-of-file + * occurs without data, or -1 on error + */ +int qio_channel_readv_all_eof(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + Error **errp); + /** * qio_channel_readv_all: * @ioc: the channel object @@ -382,6 +412,28 @@ ssize_t qio_channel_write(QIOChannel *ioc, size_t buflen, Error **errp); +/** + * qio_channel_read_all_eof: + * @ioc: the channel object + * @buf: the memory region to read data into + * @buflen: the number of bytes to @buf + * @errp: pointer to a NULL-initialized error object + * + * Reads @buflen bytes into @buf, possibly blocking or (if the + * channel is non-blocking) yielding from the current coroutine + * multiple times until the entire content is read. If end-of-file + * occurs immediately it is not an error, but if it occurs after + * data has been read it will return an error rather than a + * short-read. Otherwise behaves as qio_channel_read(). + * + * Returns: 1 if all bytes were read, 0 if end-of-file occurs + * without data, or -1 on error + */ +int qio_channel_read_all_eof(QIOChannel *ioc, + char *buf, + size_t buflen, + Error **errp); + /** * qio_channel_read_all: * @ioc: the channel object @@ -401,6 +453,7 @@ int qio_channel_read_all(QIOChannel *ioc, char *buf, size_t buflen, Error **errp); + /** * qio_channel_write_all: * @ioc: the channel object diff --git a/io/channel.c b/io/channel.c index 9e62794cab..ec4b86de7c 100644 --- a/io/channel.c +++ b/io/channel.c @@ -86,16 +86,16 @@ ssize_t qio_channel_writev_full(QIOChannel *ioc, } - -int qio_channel_readv_all(QIOChannel *ioc, - const struct iovec *iov, - size_t niov, - Error **errp) +int qio_channel_readv_all_eof(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + Error **errp) { int ret = -1; struct iovec *local_iov = g_new(struct iovec, niov); struct iovec *local_iov_head = local_iov; unsigned int nlocal_iov = niov; + bool partial = false; nlocal_iov = iov_copy(local_iov, nlocal_iov, iov, niov, @@ -114,21 +114,43 @@ int qio_channel_readv_all(QIOChannel *ioc, } else if (len < 0) { goto cleanup; } else if (len == 0) { - error_setg(errp, - "Unexpected end-of-file before all bytes were read"); + if (partial) { + error_setg(errp, + "Unexpected end-of-file before all bytes were read"); + } else { + ret = 0; + } goto cleanup; } + partial = true; iov_discard_front(&local_iov, &nlocal_iov, len); } - ret = 0; + ret = 1; cleanup: g_free(local_iov_head); return ret; } +int qio_channel_readv_all(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + Error **errp) +{ + int ret = qio_channel_readv_all_eof(ioc, iov, niov, errp); + + if (ret == 0) { + ret = -1; + error_setg(errp, + "Unexpected end-of-file before all bytes were read"); + } else if (ret == 1) { + ret = 0; + } + return ret; +} + int qio_channel_writev_all(QIOChannel *ioc, const struct iovec *iov, size_t niov, @@ -205,6 +227,16 @@ ssize_t qio_channel_write(QIOChannel *ioc, } +int qio_channel_read_all_eof(QIOChannel *ioc, + char *buf, + size_t buflen, + Error **errp) +{ + struct iovec iov = { .iov_base = buf, .iov_len = buflen }; + return qio_channel_readv_all_eof(ioc, &iov, 1, errp); +} + + int qio_channel_read_all(QIOChannel *ioc, char *buf, size_t buflen,