[libvirt] [PATCH 1/5] Enhance the streams helper to support plain file I/O

Daniel P. Berrange berrange at redhat.com
Fri Mar 18 15:12:41 UTC 2011


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 :|




More information about the libvir-list mailing list