[Libvir] take 2 [Re: write(2) may write less than the total requested

Jim Paris jim at jtan.com
Wed Feb 20 17:16:43 UTC 2008


Hi Jim, 

comments inline


Jim Meyering wrote:
> diff --git a/proxy/libvirt_proxy.c b/proxy/libvirt_proxy.c
> index d96d3db..a22ba6c 100644
> --- a/proxy/libvirt_proxy.c
> +++ b/proxy/libvirt_proxy.c
> @@ -2,7 +2,7 @@
>   * proxy_svr.c: root suid proxy server for Xen access to APIs with no
>   *              side effects from unauthenticated clients.
>   *
> - * Copyright (C) 2006, 2007 Red Hat, Inc.
> + * Copyright (C) 2006, 2007, 2008 Red Hat, Inc.
>   *
>   * See COPYING.LIB for the License of this software
>   *
> @@ -26,6 +26,7 @@
>  #include "internal.h"
> 
>  #include "proxy_internal.h"
> +#include "util.h"
>  #include "xen_internal.h"
>  #include "xend_internal.h"
>  #include "xs_internal.h"
> @@ -317,19 +318,12 @@ proxyWriteClientSocket(int nr, virProxyPacketPtr req) {
>  	return(-1);
>      }
> 
> -retry:
> -    ret = write(pollInfos[nr].fd, (char *) req, req->len);
> +    ret = safewrite(pollInfos[nr].fd, (char *) req, req->len);
>      if (ret < 0) {

Should this check (ret == req->len) instead?  safewrite() will return
an error if write() returns an error, regardless of how many bytes are
written, but it's still possible for it to return less than requested
if write() returns 0 (eof?).  The behavior of safewrite could be
adjusted to return errors in that case.


> -        if (errno == EINTR) {
> -	    if (debug > 0)
> -	        fprintf(stderr, "write socket %d to client %d interrupted\n",
> -		        pollInfos[nr].fd, nr);
> -	    goto retry;
> -	}
>          fprintf(stderr, "write %d bytes to socket %d from client %d failed\n",
>  	        req->len, pollInfos[nr].fd, nr);
> -	proxyCloseClientSocket(nr);
> -	return(-1);
> +        proxyCloseClientSocket(nr);
> +        return(-1);
>      }
>      if (ret == 0) {
>  	if (debug)
> diff --git a/qemud/qemud.c b/qemud/qemud.c
> index 3a5e44c..269e9fe 100644
> --- a/qemud/qemud.c
> +++ b/qemud/qemud.c
> @@ -118,7 +118,7 @@ static void sig_handler(int sig) {
>          return;
> 
>      origerrno = errno;
> -    r = write(sigwrite, &sigc, 1);
> +    r = safewrite(sigwrite, &sigc, 1);

write() shouldn't do a partial write for size 1, but this is
necessary anyway to help with the EINTR case.  Might want to add that
benefit to the log message, it's not just short-writes this protects against.

>      if (r == -1) {

if (r != 1)? 

>          sig_errors++;
>          sig_lasterrno = errno;
> @@ -1360,11 +1360,9 @@ static int qemudClientWriteBuf(struct qemud_server *server,
>                                 const char *data, int len) {
>      int ret;
>      if (!client->tlssession) {
> -        if ((ret = write(client->fd, data, len)) == -1) {
> -            if (errno != EAGAIN) {
> -                qemudLog (QEMUD_ERR, _("write: %s"), strerror (errno));
> -                qemudDispatchClientFailure(server, client);
> -            }
> +        if ((ret = safewrite(client->fd, data, len)) == -1) {
> +            qemudLog (QEMUD_ERR, _("write: %s"), strerror (errno));
> +            qemudDispatchClientFailure(server, client);

ret != len?

>              return -1;
>          }
>      } else {
> diff --git a/src/conf.c b/src/conf.c
> index e0ecdea..53ea993 100644
> --- a/src/conf.c
> +++ b/src/conf.c
> @@ -904,7 +904,7 @@ __virConfWriteFile(const char *filename, virConfPtr conf)
>  	goto error;
>      }
> 
> -    ret = write(fd, buf->content, buf->use);
> +    ret = safewrite(fd, buf->content, buf->use);
>      close(fd);
>      if (ret != (int) buf->use) {
>          virConfError(NULL, VIR_ERR_WRITE_FAILED, _("failed to save content"), 0);
> diff --git a/src/console.c b/src/console.c
> index 02a9c7f..1c6cba0 100644
> --- a/src/console.c
> +++ b/src/console.c
> @@ -1,7 +1,7 @@
>  /*
>   * console.c: A dumb serial console client
>   *
> - * Copyright (C) 2007 Red Hat, Inc.
> + * Copyright (C) 2007, 2008 Red Hat, Inc.
>   *
>   * This library is free software; you can redistribute it and/or
>   * modify it under the terms of the GNU Lesser General Public
> @@ -37,6 +37,7 @@
> 
>  #include "console.h"
>  #include "internal.h"
> +#include "util.h"
> 
>  /* ie  Ctrl-]  as per telnet */
>  #define CTRL_CLOSE_BRACKET '\35'
> @@ -161,7 +162,8 @@ int vshRunConsole(const char *tty) {
> 
>                  while (sent < got) {
>                      int done;
> -                    if ((done = write(destfd, buf + sent, got - sent)) <= 0) {
> +                    if ((done = safewrite(destfd, buf + sent, got - sent))
> +                        <= 0) {

!= (got - sent)?

>                          fprintf(stderr, _("failure writing output: %s\n"),
>                                  strerror(errno));
>                          goto cleanup;
> diff --git a/src/proxy_internal.c b/src/proxy_internal.c
> index bc94763..c3e50c6 100644
> --- a/src/proxy_internal.c
> +++ b/src/proxy_internal.c
> @@ -1,7 +1,7 @@
>  /*
>   * proxy_client.c: client side of the communication with the libvirt proxy.
>   *
> - * Copyright (C) 2006 Red Hat, Inc.
> + * Copyright (C) 2006, 2008 Red Hat, Inc.
>   *
>   * See COPYING.LIB for the License of this software
>   *
> @@ -26,6 +26,7 @@
>  #include "internal.h"
>  #include "driver.h"
>  #include "proxy_internal.h"
> +#include "util.h"
>  #include "xen_unified.h"
> 
>  #define STANDALONE
> @@ -345,17 +346,10 @@ virProxyWriteClientSocket(int fd, const char *data, int len) {
>      if ((fd < 0) || (data == NULL) || (len < 0))
>          return(-1);
> 
> -retry:
> -    ret = write(fd, data, len);
> +    ret = safewrite(fd, data, len);
>      if (ret < 0) {

ret != len?
Or at least (ret <= 0).

> -        if (errno == EINTR) {
> -	    if (debug > 0)
> -	        fprintf(stderr, "write socket %d, %d bytes interrupted\n",
> -		        fd, len);
> -	    goto retry;
> -	}
>          fprintf(stderr, _("Failed to write to socket %d\n"), fd);
> -	return(-1);
> +        return(-1);
>      }
>      if (debug)
>  	fprintf(stderr, "wrote %d bytes to socket %d\n",
> diff --git a/src/qemu_conf.c b/src/qemu_conf.c
> index e39c7bc..ff6a92e 100644
> --- a/src/qemu_conf.c
> +++ b/src/qemu_conf.c
> @@ -1866,7 +1866,7 @@ static int qemudSaveConfig(virConnectPtr conn,
>      }
> 
>      towrite = strlen(xml);
> -    if (write(fd, xml, towrite) != towrite) {
> +    if (safewrite(fd, xml, towrite) < 0) {

Stick with "!= towrite" or at least "<= 0"

>          qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
>                           "cannot write config file %s: %s",
>                           vm->configFile, strerror(errno));
> @@ -2089,7 +2089,7 @@ static int qemudSaveNetworkConfig(virConnectPtr conn,
>      }
> 
>      towrite = strlen(xml);
> -    if (write(fd, xml, towrite) != towrite) {
> +    if (safewrite(fd, xml, towrite) < 0) {

ditto

>          qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
>                           "cannot write config file %s: %s",
>                           network->configFile, strerror(errno));
> diff --git a/src/qemu_driver.c b/src/qemu_driver.c
> index 15cd52c..85d042f 100644
> --- a/src/qemu_driver.c
> +++ b/src/qemu_driver.c
> @@ -539,11 +539,9 @@ static int qemudWaitForMonitor(virConnectPtr conn,
>                                       "console");
> 
>      buf[sizeof(buf)-1] = '\0';
> - retry:
> -    if (write(vm->logfile, buf, strlen(buf)) < 0) {
> +
> +    if (safewrite(vm->logfile, buf, strlen(buf)) < 0) {

!= strlen(buf)

>          /* Log, but ignore failures to write logfile for VM */
> -        if (errno == EINTR)
> -            goto retry;
>          qemudLog(QEMUD_WARN, _("Unable to log VM console data: %s"),
>                   strerror(errno));
>      }
> @@ -656,15 +654,15 @@ static int qemudStartVMDaemon(virConnectPtr conn,
> 
>      tmp = argv;
>      while (*tmp) {
> -        if (write(vm->logfile, *tmp, strlen(*tmp)) < 0)
> +        if (safewrite(vm->logfile, *tmp, strlen(*tmp)) < 0)

!= strlen(*tmp)

>              qemudLog(QEMUD_WARN, _("Unable to write argv to logfile %d: %s"),
>                       errno, strerror(errno));
> -        if (write(vm->logfile, " ", 1) < 0)
> +        if (safewrite(vm->logfile, " ", 1) < 0)

!= 1

>              qemudLog(QEMUD_WARN, _("Unable to write argv to logfile %d: %s"),
>                       errno, strerror(errno));
>          tmp++;
>      }
> -    if (write(vm->logfile, "\n", 1) < 0)
> +    if (safewrite(vm->logfile, "\n", 1) < 0)

!= 1

>          qemudLog(QEMUD_WARN, _("Unable to write argv to logfile %d: %s"),
>                   errno, strerror(errno));
> 
> @@ -733,11 +731,8 @@ static int qemudVMData(struct qemud_driver *driver ATTRIBUTE_UNUSED,
>          }
>          buf[ret] = '\0';
> 
> -    retry:
> -        if (write(vm->logfile, buf, ret) < 0) {
> +        if (safewrite(vm->logfile, buf, ret) < 0) {

!= ret

>              /* Log, but ignore failures to write logfile for VM */
> -            if (errno == EINTR)
> -                goto retry;
>              qemudLog(QEMUD_WARN, _("Unable to log VM console data: %s"),
>                       strerror(errno));
>          }
> @@ -1113,7 +1108,7 @@ qemudEnableIpForwarding(void)
>      if ((fd = open(PROC_IP_FORWARD, O_WRONLY|O_TRUNC)) == -1)
>          return 0;
> 
> -    if (write(fd, "1\n", 2) < 0)
> +    if (safewrite(fd, "1\n", 2) < 0)

!= 2

>          ret = 0;
> 
>      close (fd);
> diff --git a/src/test.c b/src/test.c
> index 003d6b7..346774b 100644
> --- a/src/test.c
> +++ b/src/test.c
> @@ -42,6 +42,7 @@
>  #include "test.h"
>  #include "xml.h"
>  #include "buf.h"
> +#include "util.h"
>  #include "uuid.h"
> 
>  /* Flags that determine the action to take on a shutdown or crash of a domain
> @@ -1293,19 +1294,19 @@ static int testDomainSave(virDomainPtr domain,
>          return (-1);
>      }
>      len = strlen(xml);
> -    if (write(fd, TEST_SAVE_MAGIC, sizeof(TEST_SAVE_MAGIC)) != sizeof(TEST_SAVE_MAGIC)) {
> +    if (safewrite(fd, TEST_SAVE_MAGIC, sizeof(TEST_SAVE_MAGIC)) < 0) {

keep the != sizeof(TEST_SAVE_MAGIC)

>          testError(domain->conn, domain, NULL, VIR_ERR_INTERNAL_ERROR,
>                    "cannot write header");
>          close(fd);
>          return (-1);
>      }
> -    if (write(fd, (char*)&len, sizeof(len)) != sizeof(len)) {
> +    if (safewrite(fd, (char*)&len, sizeof(len)) < 0) {

keep the != sizeof(len)

>          testError(domain->conn, domain, NULL, VIR_ERR_INTERNAL_ERROR,
>                    "cannot write metadata length");
>          close(fd);
>          return (-1);
>      }
> -    if (write(fd, xml, len) != len) {
> +    if (safewrite(fd, xml, len) < 0) {

keep the != len

>          testError(domain->conn, domain, NULL, VIR_ERR_INTERNAL_ERROR,
>                    "cannot write metadata");
>          free(xml);
> @@ -1398,7 +1399,7 @@ static int testDomainCoreDump(virDomainPtr domain,
>                    "cannot save domain core");
>          return (-1);
>      }
> -    if (write(fd, TEST_SAVE_MAGIC, sizeof(TEST_SAVE_MAGIC)) != sizeof(TEST_SAVE_MAGIC)) {
> +    if (safewrite(fd, TEST_SAVE_MAGIC, sizeof(TEST_SAVE_MAGIC)) < 0) {

keep the != sizeof(TEST_SAVE_MAGIC)

>          testError(domain->conn, domain, NULL, VIR_ERR_INTERNAL_ERROR,
>                    "cannot write header");
>          close(fd);
> diff --git a/src/util.c b/src/util.c
> index f082984..c813a4c 100644
> --- a/src/util.c
> +++ b/src/util.c
> @@ -1,7 +1,7 @@
>  /*
>   * utils.c: common, generic utility functions
>   *
> - * Copyright (C) 2006, 2007 Red Hat, Inc.
> + * Copyright (C) 2006, 2007, 2008 Red Hat, Inc.
>   * Copyright (C) 2006 Daniel P. Berrange
>   * Copyright (C) 2006, 2007 Binary Karma
>   * Copyright (C) 2006 Shuveb Hussain
> @@ -295,26 +295,6 @@ int saferead(int fd, void *buf, size_t count)
>  	return nread;
>  }
> 
> -/* Like write(), but restarts after EINTR */
> -ssize_t safewrite(int fd, const void *buf, size_t count)
> -{
> -	size_t nwritten = 0;
> -	while (count > 0) {
> -		int r = write(fd, buf, count);
> -
> -		if (r < 0 && errno == EINTR)
> -			continue;
> -		if (r < 0)
> -			return r;
> -		if (r == 0)
> -			return nwritten;
> -		buf = (unsigned char *)buf + r;
> -		count -= r;
> -		nwritten += r;
> -	}
> -	return nwritten;
> -}
> -
> 
>  int __virFileReadAll(const char *path,
>                       int maxlen,
> diff --git a/src/util.h b/src/util.h
> index da1f5b0..218ab1b 100644
> --- a/src/util.h
> +++ b/src/util.h
> @@ -31,7 +31,26 @@ int virExecNonBlock(virConnectPtr conn, char **argv, int *retpid, int infd, int
>  int virRun(virConnectPtr conn, char **argv, int *status);
> 
>  int saferead(int fd, void *buf, size_t count);
> -ssize_t safewrite(int fd, const void *buf, size_t count);
> +
> +/* Like write(), but restarts after EINTR */
> +static inline ssize_t safewrite(int fd, const void *buf, size_t count)
> +{
> +	size_t nwritten = 0;
> +	while (count > 0) {
> +		int r = write(fd, buf, count);
> +
> +		if (r < 0 && errno == EINTR)
> +			continue;
> +		if (r < 0)
> +			return r;
> +		if (r == 0)
> +			return nwritten;
> +		buf = (unsigned char *)buf + r;
> +		count -= r;
> +		nwritten += r;
> +	}
> +	return nwritten;
> +}
> 
>  int __virFileReadAll(const char *path,
>  		     int maxlen,
> diff --git a/src/virsh.c b/src/virsh.c
> index 730f251..b67e63c 100644
> --- a/src/virsh.c
> +++ b/src/virsh.c
> @@ -4582,7 +4582,7 @@ vshOutputLogFile(vshControl *ctl, int log_level, const char *msg_format, va_list
>          snprintf(msg_buf + strlen(msg_buf), sizeof(msg_buf) - strlen(msg_buf), "\n");
> 
>      /* write log */
> -    if (write(ctl->log_fd, msg_buf, strlen(msg_buf)) == -1) {
> +    if (safewrite(ctl->log_fd, msg_buf, strlen(msg_buf)) < 0) {

!= strlen(msg_buf)

>          vshCloseLogFile(ctl);
>          vshError(ctl, FALSE, "%s", _("failed to write the log file"));
>      }
> --
> 1.5.4.2.134.g82883
> 
> --
> Libvir-list mailing list
> Libvir-list at redhat.com
> https://www.redhat.com/mailman/listinfo/libvir-list


-jim




More information about the libvir-list mailing list