
On Tue, Mar 15, 2011 at 09:53:40AM -0600, Eric Blake wrote:
On 03/15/2011 06:30 AM, Daniel P. Berrange wrote:
The O_NONBLOCK flag doesn't work as desired on plain files or block devices. Introduce an I/O helper program that does the blocking I/O operations, communicating over a pipe that can support O_NONBLOCK
* src/fdstream.c, src/fdstream.h: Add non-blocking I/O on plain files/block devices * src/Makefile.am, src/util/iohelper.c: I/O helper program * src/qemu/qemu_driver.c, src/lxc/lxc_driver.c, src/uml/uml_driver.c, src/xen/xen_driver.c: Update for streams API change
@@ -206,6 +212,25 @@ static int virFDStreamFree(struct virFDStreamData *fdst) { int ret; ret = VIR_CLOSE(fdst->fd); + if (fdst->cmd) { + int status; + if (virCommandWait(fdst->cmd, &status) < 0) + ret = -1; + if (status != 0) { + ssize_t len = 1024; + char buf[len]; + if ((len = saferead(fdst->errfd, buf, len)) < 0) {
Is this safe to read from errfd after you've already waited for the child to exit? After all, exit() is allowed to discard unread data from a pipe, and once the child is exited, the parent may get EOF instead of that data. Conversely, can the child block trying to write into errfd, and thus never reach the exit, so that you have a deadlock where the parent is waiting without reading? I think you have to read errfd prior to calling virCommandWait.
Empirically it worked fine, but I've reordered it now.
- if ((fd = open(path, flags)) < 0) { + if (flags & O_CREAT) + fd = open(path, flags, mode); + else + fd = open(path, flags);
Technically, it's safe to call fd = open(path, flags, mode) even when flags does not contain O_CREAT, but I'm fine with keeping this if statement.
Yes, I think it is nice to be explicit here.
+ const char *opname; + int childfd; + char offsetstr[100]; + char lengthstr[100]; + + snprintf(offsetstr, sizeof(offsetstr), "%llu", offset); + snprintf(lengthstr, sizeof(lengthstr), "%llu", length);
100 is too big. Use this for a tighter bound:
#include "intprops.h"
char offsetstr[INT_BUFSIZE_BOUND(offset)];
But see below for an alternate suggestion that loses these variables altogether.
I switched to virCommandAddArgFormat, since I also now have two more integer args to pass
+ + if (flags == O_RDONLY) { + opname = "read"; + } else if (flags == O_WRONLY) { + opname = "write"; + } else if (flags == (O_WRONLY|O_CREAT)) { + opname = "create"; + } else { + streamsReportError(VIR_ERR_INTERNAL_ERROR, + _("Non-blocking I/O is not supported on %s with flags %d"),
Okay, so you insist on a specific subset of flags. But what about flags like O_CLOEXEC? Should you only be comparing against the bitmask of flags that you actually care about, while allowing other flags as needed? Also, should you support O_EXCL? I guess we can add support for more flags at the point where we add more clients that want to use those flags.
I've killed this. Instead of passing an 'opname' to the command helper, I just pass the raw int value of 'flags' and 'mode' as parameters.
+ + if (!cmd) + goto error; + + //virCommandDaemonize(cmd); + if (flags == O_RDONLY) { + childfd = fds[1]; + fd = fds[0]; + virCommandSetOutputFD(cmd, &childfd); + } else { + childfd = fds[0]; + fd = fds[1]; + virCommandSetInputFD(cmd, childfd);
Should we also be setting FD_CLOEXEC on the side of the pipe not given to the child? (I really need to find time to work on O_CLOEXEC support in gnulib, then do an audit over all of libvirt to take advantage of atomic CLOEXEC support by using things like pipe2).
We've not bothered with CLOEXEC anywhere else in our code really, since we loop over all FDs and explicitly close them.
+ if (fd < 0) { + virReportSystemError(errno, _("Unable to open %s"), path); + goto cleanup; + } + +#if 0 + if (length && (flags & O_CREAT)) { + if (ftruncate(fd, length) < 0) {
Same comment about O_TRUNC support.
I removed this unused code - it was from an earlier idea that didn't play out.
+ if (argc != 5) { + fprintf(stderr, _("%s: syntax FILENAME OPERATION OFFSET LENGTH\n"), argv[0]); + exit(EXIT_FAILURE); + }
Do we want --help/--version support (and a man page), or is this program considered internal enough to not need the public-facing documentation?
It isn't intended for end users to ever run this. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|