[libvirt PATCH v2 15/16] qemu: pass sensitive data to nbdkit via pipe

Jonathon Jongsma jjongsma at redhat.com
Wed Aug 31 18:41:00 UTC 2022


Rather than passing passwords and cookies (which could contain
passwords) to nbdkit via commandline arguments, use the alternate format
that nbdkit supports where we can specify a file descriptor which nbdkit
will read to get the password or cookies.

Signed-off-by: Jonathon Jongsma <jjongsma at redhat.com>
---
 build-aux/syntax-check.mk                     |   4 +-
 src/qemu/qemu_nbdkit.c                        | 162 ++++++++++++++++--
 src/qemu/qemu_nbdkitpriv.h                    |  19 +-
 src/util/virutil.h                            |   2 +-
 .../disk-cdrom-network.args.disk1             |   2 +-
 .../disk-cdrom-network.args.disk1.pipe.45     |   1 +
 .../disk-cdrom-network.args.disk2             |   2 +-
 .../disk-cdrom-network.args.disk2.pipe.47     |   1 +
 .../disk-network-http.args.disk2              |   2 +-
 .../disk-network-http.args.disk2.pipe.45      |   1 +
 .../disk-network-http.args.disk3              |   2 +-
 .../disk-network-http.args.disk3.pipe.47      |   1 +
 ...work-source-curl-nbdkit-backing.args.disk0 |   2 +-
 ...rce-curl-nbdkit-backing.args.disk0.pipe.45 |   1 +
 .../disk-network-source-curl.args.disk0       |   2 +-
 ...isk-network-source-curl.args.disk0.pipe.45 |   1 +
 .../disk-network-source-curl.args.disk1       |   2 +-
 ...isk-network-source-curl.args.disk1.pipe.47 |   1 +
 .../disk-network-source-curl.args.disk2       |   2 +-
 ...isk-network-source-curl.args.disk2.pipe.49 |   1 +
 tests/qemunbdkittest.c                        |  57 +++++-
 21 files changed, 238 insertions(+), 30 deletions(-)
 create mode 100644 tests/qemunbdkitdata/disk-cdrom-network.args.disk1.pipe.45
 create mode 100644 tests/qemunbdkitdata/disk-cdrom-network.args.disk2.pipe.47
 create mode 100644 tests/qemunbdkitdata/disk-network-http.args.disk2.pipe.45
 create mode 100644 tests/qemunbdkitdata/disk-network-http.args.disk3.pipe.47
 create mode 100644 tests/qemunbdkitdata/disk-network-source-curl-nbdkit-backing.args.disk0.pipe.45
 create mode 100644 tests/qemunbdkitdata/disk-network-source-curl.args.disk0.pipe.45
 create mode 100644 tests/qemunbdkitdata/disk-network-source-curl.args.disk1.pipe.47
 create mode 100644 tests/qemunbdkitdata/disk-network-source-curl.args.disk2.pipe.49

diff --git a/build-aux/syntax-check.mk b/build-aux/syntax-check.mk
index 649eb91acb..c0cf730d13 100644
--- a/build-aux/syntax-check.mk
+++ b/build-aux/syntax-check.mk
@@ -1363,10 +1363,10 @@ exclude_file_name_regexp--sc_prohibit_strdup = \
   ^(docs/|examples/|tests/virnetserverclientmock.c|tests/commandhelper.c|tools/nss/libvirt_nss_(leases|macs)\.c$$)
 
 exclude_file_name_regexp--sc_prohibit_close = \
-  (\.p[yl]$$|\.spec\.in$$|^docs/|^(src/util/vir(file|event)\.c|src/libvirt-stream\.c|tests/(vir.+mock\.c|commandhelper\.c|qemusecuritymock\.c)|tools/nss/libvirt_nss_(leases|macs)\.c)$$)
+  (\.p[yl]$$|\.spec\.in$$|^docs/|^(src/util/vir(file|event)\.c|src/libvirt-stream\.c|tests/(vir.+mock\.c|commandhelper\.c|qemusecuritymock\.c|qemunbdkittest\.c)|tools/nss/libvirt_nss_(leases|macs)\.c)$$)
 
 exclude_file_name_regexp--sc_prohibit_empty_lines_at_EOF = \
-  (^tests/(nodedevmdevctl|virhostcpu|virpcitest|virstoragetest)data/|docs/js/.*\.js|docs/fonts/.*\.woff|\.diff|tests/virconfdata/no-newline\.conf$$)
+  (^tests/(nodedevmdevctl|virhostcpu|virpcitest|virstoragetest|qemunbdkit)data/|docs/js/.*\.js|docs/fonts/.*\.woff|\.diff|tests/virconfdata/no-newline\.conf$$)
 
 exclude_file_name_regexp--sc_prohibit_fork_wrappers = \
   (^(src/(util/(vircommand|virdaemon)|lxc/lxc_controller)|tests/testutils)\.c$$)
diff --git a/src/qemu/qemu_nbdkit.c b/src/qemu/qemu_nbdkit.c
index 0ecf6c6537..2b8e203d16 100644
--- a/src/qemu/qemu_nbdkit.c
+++ b/src/qemu/qemu_nbdkit.c
@@ -55,6 +55,76 @@ VIR_ENUM_IMPL(qemuNbdkitCaps,
     "filter-readahead", /* QEMU_NBDKIT_CAPS_FILTER_READAHEAD */
 );
 
+
+static void
+nbdkitPipeItemFree(NbdkitPipeItem *item)
+{
+    if (item->buf) {
+        virSecureErase(item->buf, item->buflen);
+        g_free(item->buf);
+    }
+
+    if (item->fd > 0)
+        VIR_FORCE_CLOSE(item->fd);
+
+    g_free(item);
+}
+
+
+void nbdkitPipeDataFree(NbdkitPipeData *self)
+{
+    size_t i;
+
+    if (!self)
+        return;
+
+    for (i = 0; i < self->nitems; i++) {
+        nbdkitPipeItemFree(self->items[i]);
+    }
+
+    g_free(self->items);
+    g_free(self);
+}
+
+
+static NbdkitPipeItem*
+nbdkitPipeItemNew(int fd, void *data, int datalen)
+{
+    NbdkitPipeItem *d;
+
+    if (!data || datalen == 0)
+        return NULL;
+
+    d = g_new0(NbdkitPipeItem, 1);
+    d->fd = fd;
+
+    if (datalen < 0) {
+        /* -1 indicates a null-terminated string */
+        d->buf = g_strdup(data);
+        d->buflen = strlen(data);
+    } else {
+        d->buf = g_malloc(datalen);
+        memcpy(d->buf, data, datalen);
+        d->buflen = datalen;
+    }
+
+    return d;
+}
+
+
+static int
+nbdkitPipeDataWrite(NbdkitPipeItem *pipe)
+{
+    if (safewrite(pipe->fd, pipe->buf, pipe->buflen) < 0) {
+        virReportSystemError(errno,
+                             _("failed to write data to pipe %i for nbdkit"),
+                             pipe->fd);
+        return -1;
+    }
+    return 0;
+}
+
+
 struct _qemuNbdkitCaps {
     GObject parent;
 
@@ -71,6 +141,12 @@ struct _qemuNbdkitCaps {
 G_DEFINE_TYPE(qemuNbdkitCaps, qemu_nbdkit_caps, G_TYPE_OBJECT);
 
 
+enum {
+    PIPE_FD_READ = 0,
+    PIPE_FD_WRITE = 1
+};
+
+
 static void
 qemuNbdkitCheckCommandCap(qemuNbdkitCaps *nbdkit,
                           virCommand *cmd,
@@ -685,12 +761,36 @@ qemuNbdkitInitStorageSource(qemuNbdkitCaps *caps,
 }
 
 
+static NbdkitPipeItem*
+commandPassDataByPipe(virCommand *cmd,
+                      const char *argName,
+                      char *buf,
+                      size_t buflen)
+{
+    int fds[2] = { -1, -1 };
+    g_autofree char *fdfmt = NULL;
+
+    if (virPipe(fds) < 0)
+        return NULL;
+
+    /* some nbdkit arguments accept a variation where nbdkit will read the data
+     * from a file descriptor, e.g. password=-FD */
+    fdfmt = g_strdup_printf("-%i", fds[PIPE_FD_READ]);
+    virCommandAddArgPair(cmd, argName, fdfmt);
+    virCommandPassFD(cmd, fds[PIPE_FD_READ], VIR_COMMAND_PASS_FD_CLOSE_PARENT);
+
+    return nbdkitPipeItemNew(fds[PIPE_FD_WRITE], (char*)buf, buflen);
+}
+
 static int
 qemuNbdkitProcessBuildCommandCurl(qemuNbdkitProcess *proc,
-                                  virCommand *cmd)
+                                  virCommand *cmd,
+                                  NbdkitPipeData **pipeData)
 {
     g_autoptr(virURI) uri = qemuBlockStorageSourceGetURI(proc->source);
     g_autofree char *uristring = virURIFormat(uri);
+    g_autoptr(GPtrArray) pipes =
+        g_ptr_array_new_with_free_func((GDestroyNotify)nbdkitPipeDataFree);
 
     /* nbdkit plugin name */
     virCommandAddArg(cmd, "curl");
@@ -702,8 +802,9 @@ qemuNbdkitProcessBuildCommandCurl(qemuNbdkitProcess *proc,
         g_autoptr(virConnect) conn = virGetConnectSecret();
         g_autofree uint8_t *secret = NULL;
         size_t secretlen = 0;
-        g_autofree char *password = NULL;
         int secrettype;
+        virStorageAuthDef *authdef = proc->source->auth;
+        NbdkitPipeItem *pipe = NULL;
 
         virCommandAddArgPair(cmd, "user",
                              proc->source->auth->username);
@@ -716,7 +817,7 @@ qemuNbdkitProcessBuildCommandCurl(qemuNbdkitProcess *proc,
         }
 
         if (virSecretGetSecretString(conn,
-                                     &proc->source->auth->seclookupdef,
+                                     &authdef->seclookupdef,
                                      secrettype,
                                      &secret,
                                      &secretlen) < 0) {
@@ -725,18 +826,28 @@ qemuNbdkitProcessBuildCommandCurl(qemuNbdkitProcess *proc,
             return -1;
         }
 
-        /* ensure that the secret is a NULL-terminated string */
-        password = g_strndup((char*)secret, secretlen);
-
-        virCommandAddArgPair(cmd, "password", password);
+        if (!(pipe = commandPassDataByPipe(cmd, "password", (char*)secret,
+                                           secretlen))) {
+            virSecureErase(secret, secretlen);
+            return -1;
+        }
 
         virSecureErase(secret, secretlen);
-        virSecureErase(password, secretlen);
+        g_ptr_array_add(pipes, pipe);
     }
 
-    if (proc->source->ncookies > 0)
-        virCommandAddArgPair(cmd, "cookie",
-                             qemuBlockStorageSourceGetCookieString(proc->source));
+    /* Create a pipe to send the cookies to the nbdkit process. */
+    if (proc->source->ncookies) {
+        NbdkitPipeItem *pipe = NULL;
+        g_autofree char *cookies =
+            qemuBlockStorageSourceGetCookieString(proc->source);
+
+        if (!(pipe = commandPassDataByPipe(cmd, "cookie", cookies, -1))) {
+            return -1;
+        }
+
+        g_ptr_array_add(pipes, pipe);
+    }
 
     if (proc->source->sslverify == VIR_TRISTATE_BOOL_NO) {
         virCommandAddArgPair(cmd, "sslverify", "false");
@@ -747,6 +858,10 @@ qemuNbdkitProcessBuildCommandCurl(qemuNbdkitProcess *proc,
         virCommandAddArgPair(cmd, "timeout", timeout);
     }
 
+    *pipeData = g_new0(NbdkitPipeData, 1);
+    (*pipeData)->nitems = pipes->len;
+    (*pipeData)->items = (NbdkitPipeItem**)g_ptr_array_free(g_steal_pointer(&pipes), false);
+
     return 0;
 }
 
@@ -781,8 +896,17 @@ qemuNbdkitProcessBuildCommandSSH(qemuNbdkitProcess *proc,
 }
 
 
+/* Builds a nbdkit command for the given disk source.
+ *
+ * Some sensitive data should be not be passed to nbdkit via commandline, so
+ * this command may set up one or more pipes and pass the fd of these pipes to
+ * the nbdkit command. In that case, pipeData will return information about the
+ * pipes and the information that must be written to that pipe (via
+ * nbdkitPipeDataWrite()) after the command has been executed. The pipeData
+ * elements should be freed after writing. */
 virCommand *
-qemuNbdkitProcessBuildCommand(qemuNbdkitProcess *proc)
+qemuNbdkitProcessBuildCommand(qemuNbdkitProcess *proc,
+                              NbdkitPipeData **pipeData)
 {
     g_autoptr(virCommand) cmd = virCommandNewArgList(proc->caps->path,
                                                      "--exit-with-parent",
@@ -806,7 +930,7 @@ qemuNbdkitProcessBuildCommand(qemuNbdkitProcess *proc)
         case VIR_STORAGE_NET_PROTOCOL_FTP:
         case VIR_STORAGE_NET_PROTOCOL_FTPS:
         case VIR_STORAGE_NET_PROTOCOL_TFTP:
-            if (qemuNbdkitProcessBuildCommandCurl(proc, cmd) < 0)
+            if (qemuNbdkitProcessBuildCommandCurl(proc, cmd, pipeData) < 0)
                 return NULL;
             break;
         case VIR_STORAGE_NET_PROTOCOL_SSH:
@@ -869,8 +993,10 @@ qemuNbdkitProcessStart(qemuNbdkitProcess *proc,
     int exitstatus = 0;
     int cmdret = 0;
     int errfd = -1;
+    g_autoptr(NbdkitPipeData) pipes = NULL;
+    size_t i;
 
-    if (!(cmd = qemuNbdkitProcessBuildCommand(proc)))
+    if (!(cmd = qemuNbdkitProcessBuildCommand(proc, &pipes)))
         return -1;
 
     virCommandSetErrorFD(cmd, &errfd);
@@ -888,6 +1014,14 @@ qemuNbdkitProcessStart(qemuNbdkitProcess *proc,
         goto error;
     }
 
+    if (pipes) {
+        for (i = 0; i < pipes->nitems; i++) {
+            NbdkitPipeItem *pipe = pipes->items[i];
+            if (nbdkitPipeDataWrite(pipe) < 0)
+                goto error;
+        }
+    }
+
     if ((rc = virPidFileReadPath(proc->pidfile, &proc->pid)) < 0) {
         virReportSystemError(-rc,
                              _("Failed to read pidfile %s"),
diff --git a/src/qemu/qemu_nbdkitpriv.h b/src/qemu/qemu_nbdkitpriv.h
index 64f9bb99d8..a4cc61bb2c 100644
--- a/src/qemu/qemu_nbdkitpriv.h
+++ b/src/qemu/qemu_nbdkitpriv.h
@@ -27,5 +27,20 @@
 
 #include "qemu_nbdkit.h"
 
-virCommand *
-qemuNbdkitProcessBuildCommand(qemuNbdkitProcess *proc);
+typedef struct {
+    int fd;
+    void *buf;
+    size_t buflen;
+} NbdkitPipeItem;
+
+typedef struct {
+    size_t nitems;
+    NbdkitPipeItem **items;
+} NbdkitPipeData;
+
+void nbdkitPipeDataFree(NbdkitPipeData *pipedata);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(NbdkitPipeData, nbdkitPipeDataFree);
+
+virCommand* qemuNbdkitProcessBuildCommand(qemuNbdkitProcess *proc,
+                                          NbdkitPipeData **pipeData);
diff --git a/src/util/virutil.h b/src/util/virutil.h
index ab8511bf8d..094b399859 100644
--- a/src/util/virutil.h
+++ b/src/util/virutil.h
@@ -186,7 +186,7 @@ char *virGetPassword(void);
  *
  * Returns: -1 on error, 0 on success
  */
-int virPipe(int fds[2]);
+int virPipe(int fds[2]) G_NO_INLINE;
 
 /*
  * virPipeQuiet:
diff --git a/tests/qemunbdkitdata/disk-cdrom-network.args.disk1 b/tests/qemunbdkitdata/disk-cdrom-network.args.disk1
index 257e331db8..da9e507f1b 100644
--- a/tests/qemunbdkitdata/disk-cdrom-network.args.disk1
+++ b/tests/qemunbdkitdata/disk-cdrom-network.args.disk1
@@ -6,4 +6,4 @@ nbdkit \
 protocols=ftps \
 url=ftps://host.name:990/url/path/file.iso \
 user=testuser \
-password=iscsi-mycluster_myname-secret
+password=-44
diff --git a/tests/qemunbdkitdata/disk-cdrom-network.args.disk1.pipe.45 b/tests/qemunbdkitdata/disk-cdrom-network.args.disk1.pipe.45
new file mode 100644
index 0000000000..ccdd4033fc
--- /dev/null
+++ b/tests/qemunbdkitdata/disk-cdrom-network.args.disk1.pipe.45
@@ -0,0 +1 @@
+iscsi-mycluster_myname-secret
\ No newline at end of file
diff --git a/tests/qemunbdkitdata/disk-cdrom-network.args.disk2 b/tests/qemunbdkitdata/disk-cdrom-network.args.disk2
index f7879a9f24..0742b29853 100644
--- a/tests/qemunbdkitdata/disk-cdrom-network.args.disk2
+++ b/tests/qemunbdkitdata/disk-cdrom-network.args.disk2
@@ -6,4 +6,4 @@ nbdkit \
 protocols=https \
 'url=https://host.name:443/url/path/file.iso?test=val' \
 user=testuser \
-password=iscsi-mycluster_myname-secret
+password=-46
diff --git a/tests/qemunbdkitdata/disk-cdrom-network.args.disk2.pipe.47 b/tests/qemunbdkitdata/disk-cdrom-network.args.disk2.pipe.47
new file mode 100644
index 0000000000..ccdd4033fc
--- /dev/null
+++ b/tests/qemunbdkitdata/disk-cdrom-network.args.disk2.pipe.47
@@ -0,0 +1 @@
+iscsi-mycluster_myname-secret
\ No newline at end of file
diff --git a/tests/qemunbdkitdata/disk-network-http.args.disk2 b/tests/qemunbdkitdata/disk-network-http.args.disk2
index 7286b684a8..767ea881d8 100644
--- a/tests/qemunbdkitdata/disk-network-http.args.disk2
+++ b/tests/qemunbdkitdata/disk-network-http.args.disk2
@@ -4,4 +4,4 @@ nbdkit \
 --foreground curl \
 protocols=http \
 url=http://example.org:1234/test3.img \
-'cookie=test=testcookievalue; test2="blurb"'
+cookie=-44
diff --git a/tests/qemunbdkitdata/disk-network-http.args.disk2.pipe.45 b/tests/qemunbdkitdata/disk-network-http.args.disk2.pipe.45
new file mode 100644
index 0000000000..2c42c95930
--- /dev/null
+++ b/tests/qemunbdkitdata/disk-network-http.args.disk2.pipe.45
@@ -0,0 +1 @@
+test=testcookievalue; test2="blurb"
\ No newline at end of file
diff --git a/tests/qemunbdkitdata/disk-network-http.args.disk3 b/tests/qemunbdkitdata/disk-network-http.args.disk3
index da177c9e6d..30dfd15861 100644
--- a/tests/qemunbdkitdata/disk-network-http.args.disk3
+++ b/tests/qemunbdkitdata/disk-network-http.args.disk3
@@ -4,5 +4,5 @@ nbdkit \
 --foreground curl \
 protocols=https \
 'url=https://example.org:1234/test4.img?par=val&other=ble' \
-'cookie=test=testcookievalue; test2="blurb"' \
+cookie=-46 \
 sslverify=false
diff --git a/tests/qemunbdkitdata/disk-network-http.args.disk3.pipe.47 b/tests/qemunbdkitdata/disk-network-http.args.disk3.pipe.47
new file mode 100644
index 0000000000..2c42c95930
--- /dev/null
+++ b/tests/qemunbdkitdata/disk-network-http.args.disk3.pipe.47
@@ -0,0 +1 @@
+test=testcookievalue; test2="blurb"
\ No newline at end of file
diff --git a/tests/qemunbdkitdata/disk-network-source-curl-nbdkit-backing.args.disk0 b/tests/qemunbdkitdata/disk-network-source-curl-nbdkit-backing.args.disk0
index b13f5ed628..d5ad545cdc 100644
--- a/tests/qemunbdkitdata/disk-network-source-curl-nbdkit-backing.args.disk0
+++ b/tests/qemunbdkitdata/disk-network-source-curl-nbdkit-backing.args.disk0
@@ -5,4 +5,4 @@ nbdkit \
 --readonly curl \
 protocols=https \
 url=https://https.example.org:8443/path/to/disk1.qcow2 \
-'cookie=cookie1=cookievalue1; cookie2=cookievalue2'
+cookie=-44
diff --git a/tests/qemunbdkitdata/disk-network-source-curl-nbdkit-backing.args.disk0.pipe.45 b/tests/qemunbdkitdata/disk-network-source-curl-nbdkit-backing.args.disk0.pipe.45
new file mode 100644
index 0000000000..20af4ae383
--- /dev/null
+++ b/tests/qemunbdkitdata/disk-network-source-curl-nbdkit-backing.args.disk0.pipe.45
@@ -0,0 +1 @@
+cookie1=cookievalue1; cookie2=cookievalue2
\ No newline at end of file
diff --git a/tests/qemunbdkitdata/disk-network-source-curl.args.disk0 b/tests/qemunbdkitdata/disk-network-source-curl.args.disk0
index 6de42c626f..3ea686e14f 100644
--- a/tests/qemunbdkitdata/disk-network-source-curl.args.disk0
+++ b/tests/qemunbdkitdata/disk-network-source-curl.args.disk0
@@ -5,4 +5,4 @@ nbdkit \
 --readonly curl \
 protocols=https \
 url=https://https.example.org:8443/path/to/disk1.iso \
-'cookie=cookie1=cookievalue1; cookie2=cookievalue2'
+cookie=-44
diff --git a/tests/qemunbdkitdata/disk-network-source-curl.args.disk0.pipe.45 b/tests/qemunbdkitdata/disk-network-source-curl.args.disk0.pipe.45
new file mode 100644
index 0000000000..20af4ae383
--- /dev/null
+++ b/tests/qemunbdkitdata/disk-network-source-curl.args.disk0.pipe.45
@@ -0,0 +1 @@
+cookie1=cookievalue1; cookie2=cookievalue2
\ No newline at end of file
diff --git a/tests/qemunbdkitdata/disk-network-source-curl.args.disk1 b/tests/qemunbdkitdata/disk-network-source-curl.args.disk1
index 9abc1578dd..fb77794b56 100644
--- a/tests/qemunbdkitdata/disk-network-source-curl.args.disk1
+++ b/tests/qemunbdkitdata/disk-network-source-curl.args.disk1
@@ -4,5 +4,5 @@ nbdkit \
 --foreground curl \
 protocols=https \
 'url=https://https.example.org:8443/path/to/disk5.iso?foo=bar' \
-'cookie=cookie1=cookievalue1; cookie2=cookievalue2' \
+cookie=-46 \
 sslverify=false
diff --git a/tests/qemunbdkitdata/disk-network-source-curl.args.disk1.pipe.47 b/tests/qemunbdkitdata/disk-network-source-curl.args.disk1.pipe.47
new file mode 100644
index 0000000000..20af4ae383
--- /dev/null
+++ b/tests/qemunbdkitdata/disk-network-source-curl.args.disk1.pipe.47
@@ -0,0 +1 @@
+cookie1=cookievalue1; cookie2=cookievalue2
\ No newline at end of file
diff --git a/tests/qemunbdkitdata/disk-network-source-curl.args.disk2 b/tests/qemunbdkitdata/disk-network-source-curl.args.disk2
index 1ce11ce618..eab66746ef 100644
--- a/tests/qemunbdkitdata/disk-network-source-curl.args.disk2
+++ b/tests/qemunbdkitdata/disk-network-source-curl.args.disk2
@@ -5,4 +5,4 @@ nbdkit \
 --readonly curl \
 protocols=http \
 url=http://http.example.org:8080/path/to/disk2.iso \
-'cookie=cookie1=cookievalue1; cookie2=cookievalue2; cookie3=cookievalue3'
+cookie=-48
diff --git a/tests/qemunbdkitdata/disk-network-source-curl.args.disk2.pipe.49 b/tests/qemunbdkitdata/disk-network-source-curl.args.disk2.pipe.49
new file mode 100644
index 0000000000..5c035e84c5
--- /dev/null
+++ b/tests/qemunbdkitdata/disk-network-source-curl.args.disk2.pipe.49
@@ -0,0 +1 @@
+cookie1=cookievalue1; cookie2=cookievalue2; cookie3=cookievalue3
\ No newline at end of file
diff --git a/tests/qemunbdkittest.c b/tests/qemunbdkittest.c
index c53601dac8..a18567e9b4 100644
--- a/tests/qemunbdkittest.c
+++ b/tests/qemunbdkittest.c
@@ -13,6 +13,7 @@
 #include "virutil.h"
 #include "virsecret.h"
 #include "datatypes.h"
+#include "virmock.h"
 
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
@@ -20,6 +21,41 @@ static virQEMUDriver driver;
 
 
 /* Some mock implementations for testing */
+#define PIPE_FD_START 44
+static int mockpipefd = PIPE_FD_START;
+int
+virPipe(int fds[2])
+{
+    fds[0] = mockpipefd++;
+    fds[1] = mockpipefd++;
+
+    return 0;
+}
+
+static int (*real_close)(int fd);
+static void
+init_syms(void)
+{
+    VIR_MOCK_REAL_INIT(close);
+}
+
+int
+close(int fd)
+{
+    int ret;
+
+    init_syms();
+
+    if (fd >= PIPE_FD_START)
+        ret = 0;
+    else
+        ret = real_close(fd);
+
+    return ret;
+}
+
+
+
 int
 virSecretGetSecretString(virConnectPtr conn G_GNUC_UNUSED,
                          virSecretLookupTypeDef *seclookupdef,
@@ -124,6 +160,9 @@ testNbdkit(const void *data)
     size_t i;
     int ret = 0;
 
+    /* restart mock pipe fds so tests are consistent */
+    mockpipefd = PIPE_FD_START;
+
     if (!virFileExists(info->infile)) {
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        "Test input file '%s' is missing", info->infile);
@@ -149,9 +188,11 @@ testNbdkit(const void *data)
             g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew();
             g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
             const char *actualCmdline = NULL;
+            g_autoptr(NbdkitPipeData) pipes = NULL;
+            size_t j;
 
             virCommandSetDryRun(dryRunToken, &buf, true, true, NULL, NULL);
-            cmd = qemuNbdkitProcessBuildCommand(srcPriv->nbdkitProcess);
+            cmd = qemuNbdkitProcessBuildCommand(srcPriv->nbdkitProcess, &pipes);
 
             if (virCommandRun(cmd, NULL) < 0) {
                 ret = -1;
@@ -163,9 +204,19 @@ testNbdkit(const void *data)
                 continue;
             }
 
-            if (virTestCompareToFileFull(actualCmdline, cmdfile, false) < 0) {
+            if (virTestCompareToFileFull(actualCmdline, cmdfile, false) < 0)
                 ret = -1;
-                continue;
+
+            if (pipes) {
+                for (j = 0; j < pipes->nitems; j++) {
+                    NbdkitPipeItem *item = pipes->items[j];
+                    g_autofree char *pipefile = g_strdup_printf("%s.pipe.%i",
+                                                                cmdfile,
+                                                                item->fd);
+
+                    if (virTestCompareToFile(item->buf, pipefile) < 0)
+                        ret = -1;
+                }
             }
         } else {
             if (virFileExists(cmdfile)) {
-- 
2.37.1



More information about the libvir-list mailing list