[libvirt] [PATCH 4/4] iohelper: fix reading with O_DIRECT
Michal Privoznik
mprivozn at redhat.com
Tue Sep 19 09:36:48 UTC 2017
On 09/07/2017 09:44 AM, Nikolay Shirokovskiy wrote:
> saferead is not suitable for direct reads. If file size is not multiple
> of align size then we get EINVAL on the read(2) that is supposed to
> return 0 because read buffer will not be aligned at this point.
>
> Let's not read again after partial read and check that we read
> everything by comparing the number of total bytes read against file size.
> ---
> src/util/iohelper.c | 40 +++++++++++++++++++++++++++++++++++-----
> 1 file changed, 35 insertions(+), 5 deletions(-)
>
> diff --git a/src/util/iohelper.c b/src/util/iohelper.c
> index fe15a92..32c5a89 100644
> --- a/src/util/iohelper.c
> +++ b/src/util/iohelper.c
> @@ -78,10 +78,22 @@ runIO(const char *path, int fd, int oflags)
> fdoutname = "stdout";
> /* To make the implementation simpler, we give up on any
> * attempt to use O_DIRECT in a non-trivial manner. */
> - if (direct && ((end = lseek(fd, 0, SEEK_CUR)) != 0)) {
> - virReportSystemError(end < 0 ? errno : EINVAL, "%s",
> - _("O_DIRECT read needs entire seekable file"));
> - goto cleanup;
> + if (direct) {
> + if ((end = lseek(fd, 0, SEEK_CUR)) != 0) {
> + virReportSystemError(end < 0 ? errno : EINVAL, "%s",
> + _("O_DIRECT read needs entire seekable file"));
> + goto cleanup;
> + }
> +
> + if ((end = lseek(fd, 0, SEEK_END)) < 0) {
> + virReportSystemError(errno, "%s", _("can not seek file end"));
> + goto cleanup;
> + }
> +
> + if (lseek(fd, 0, SEEK_SET) < 0) {
> + virReportSystemError(errno, "%s", _("can not seek file begin"));
> + goto cleanup;
> + }
This might change the position in the file. I mean, currently there is
no way that the @fd is not set at its beginning. However, I'd like to
have iohelper generic enough so that if a lseek()-ed fd was passed
iohelper reads from there and not reposition itself.
Also, @end is rewritten here twice. It's confusing.
What we can do here is:
cur = lseek(fd, 0, SEEK_CUR);
end = lseek(fd, 0, SEEK_END);
lseek(fd, cur, SEEK_SET);
(with proper error handling obviously)
> }
> break;
> case O_WRONLY:
> @@ -109,7 +121,25 @@ runIO(const char *path, int fd, int oflags)
> while (1) {
> ssize_t got;
>
> - if ((got = saferead(fdin, buf, buflen)) < 0) {
> + /* in case of O_DIRECT we cannot read again to check for EOF
> + * after partial buffer read as it is done in saferead */
> + if (direct && fdin == fd && end - total < buflen) {
> + if (total == end)
> + break;
> +
> + while ((got = read(fd, buf, buflen)) < 0 && errno == EINTR);
In this case, the semicolon should be on a separate line:
while ()
;
> +
> + if (got < end - total) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("Unable to read last chunk from %s"),
> + fdinname);
> + goto cleanup;
> + }
> + } else {
> + got = saferead(fdin, buf, buflen);
> + }
> +
> + if (got < 0) {
> virReportSystemError(errno, _("Unable to read %s"), fdinname);
> goto cleanup;
> }
>
Otherwise looking good. Also, ACK to patches 1-3. I'll merge them
shortly so that you can send v2 just for this one.
Michal
More information about the libvir-list
mailing list