diff options
author | Richard W.M. Jones <rjones@redhat.com> | 2010-12-01 13:24:23 +0000 |
---|---|---|
committer | Richard W.M. Jones <rjones@redhat.com> | 2010-12-01 13:35:32 +0000 |
commit | 8bfca99b9ab5774ce8aa1086184479ebb98236b2 (patch) | |
tree | 295b226c25b53dac545d926ebe834ec9df432e80 | |
parent | 7e523077d650cfb71044d5e74aa8fe1f91c019ae (diff) | |
download | libguestfs-8bfca99b9ab5774ce8aa1086184479ebb98236b2.tar.gz libguestfs-8bfca99b9ab5774ce8aa1086184479ebb98236b2.tar.xz libguestfs-8bfca99b9ab5774ce8aa1086184479ebb98236b2.zip |
protocol: Really read 4 bytes while checking for cancellation.
We've not actually hit this bug in practice, but at least in
theory while checking for cancellation we could read > 0 but
fewer than 4 bytes, which would effectively be discarded and
we would lose synchronization.
Note the socket is non-blocking.
Change the code so that we temporarily set the socket back to
blocking and force the read of all 4 bytes.
-rw-r--r-- | src/proto.c | 59 |
1 files changed, 49 insertions, 10 deletions
diff --git a/src/proto.c b/src/proto.c index a2a5a15a..dd81f489 100644 --- a/src/proto.c +++ b/src/proto.c @@ -245,11 +245,56 @@ read_log_message_or_eof (guestfs_h *g, int fd, int error_if_eof) return 0; } +/* Read 'n' bytes, setting the socket to blocking temporarily so + * that we really read the number of bytes requested. + * Returns: 0 == EOF while reading + * -1 == error, error() function has been called + * n == read 'n' bytes in full + */ +static ssize_t +really_read_from_socket (guestfs_h *g, int sock, char *buf, size_t n) +{ + long flags; + ssize_t r; + size_t got; + + /* Set socket to blocking. */ + flags = fcntl (sock, F_GETFL); + if (flags == -1) { + perrorf (g, "fcntl"); + return -1; + } + if (fcntl (sock, F_SETFL, flags & ~O_NONBLOCK) == -1) { + perrorf (g, "fcntl"); + return -1; + } + + got = 0; + while (got < n) { + r = read (sock, &buf[got], n-got); + if (r == -1) { + perrorf (g, "read"); + return -1; + } + if (r == 0) + return 0; /* EOF */ + got += r; + } + + /* Restore original socket flags. */ + if (fcntl (sock, F_SETFL, flags) == -1) { + perrorf (g, "fcntl"); + return -1; + } + + return (ssize_t) got; +} + static int check_for_daemon_cancellation_or_eof (guestfs_h *g, int fd) { char buf[4]; - int n; + ssize_t n; uint32_t flag; XDR xdr; @@ -258,21 +303,15 @@ check_for_daemon_cancellation_or_eof (guestfs_h *g, int fd) "check_for_daemon_cancellation_or_eof: %p g->state = %d, fd = %d\n", g, g->state, fd); - n = read (fd, buf, 4); + n = really_read_from_socket (g, fd, buf, 4); + if (n == -1) + return -1; if (n == 0) { /* Hopefully this indicates the qemu child process has died. */ child_cleanup (g); return -1; } - if (n == -1) { - if (errno == EINTR || errno == EAGAIN) - return 0; - - perrorf (g, "read"); - return -1; - } - xdrmem_create (&xdr, buf, 4, XDR_DECODE); xdr_uint32_t (&xdr, &flag); xdr_destroy (&xdr); |