[libvirt] [PATCH v2 3/5] Add a helper API for setting up a NBD device with qemu-nbd

Daniel P. Berrange berrange at redhat.com
Mon Apr 22 14:06:16 UTC 2013


From: "Daniel P. Berrange" <berrange at redhat.com>

Add a virFileNBDDeviceAssociate method, which given a filename
will setup a NBD device, using qemu-nbd as the server.

Signed-off-by: Daniel P. Berrange <berrange at redhat.com>
---
 src/libvirt_private.syms |   1 +
 src/util/virfile.c       | 151 ++++++++++++++++++++++++++++++++++++++++++++++-
 src/util/virfile.h       |   6 ++
 3 files changed, 155 insertions(+), 3 deletions(-)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index f778e9c..a857a0b 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1272,6 +1272,7 @@ virFileDirectFdFlag;
 virFileFclose;
 virFileFdopen;
 virFileLoopDeviceAssociate;
+virFileNBDDeviceAssociate;
 virFileRewrite;
 virFileTouch;
 virFileUpdatePerm;
diff --git a/src/util/virfile.c b/src/util/virfile.c
index fbaeedd..034648b 100644
--- a/src/util/virfile.c
+++ b/src/util/virfile.c
@@ -530,6 +530,7 @@ static int virFileLoopDeviceOpen(char **dev_name)
         goto cleanup;
     }
 
+    errno = 0;
     while ((de = readdir(dh)) != NULL) {
         if (!STRPREFIX(de->d_name, "loop"))
             continue;
@@ -561,10 +562,15 @@ static int virFileLoopDeviceOpen(char **dev_name)
         /* Oh well, try the next device */
         VIR_FORCE_CLOSE(fd);
         VIR_FREE(looppath);
+        errno = 0;
     }
 
-    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                   _("Unable to find a free loop device in /dev"));
+    if (errno != 0)
+        virReportSystemError(errno, "%s",
+                             _("Unable to iterate over loop devices"));
+    else
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Unable to find a free loop device in /dev"));
 
 cleanup:
     if (fd != -1) {
@@ -631,10 +637,138 @@ cleanup:
     return lofd;
 }
 
+
+# define SYSFS_BLOCK_DIR "/sys/block"
+
+
+static int
+virFileNBDDeviceIsBusy(const char *devname)
+{
+    char *path;
+    int ret = -1;
+
+    if (virAsprintf(&path, SYSFS_BLOCK_DIR "/%s/pid",
+                    devname) < 0) {
+        virReportOOMError();
+        return -1;
+    }
+
+    if (access(path, F_OK) < 0) {
+        if (errno == ENOENT)
+            ret = 0;
+        else
+            virReportSystemError(errno,
+                                 _("Cannot check NBD device %s pid"),
+                                 devname);
+        goto cleanup;
+    }
+    ret = 1;
+
+cleanup:
+    VIR_FREE(path);
+    return ret;
+}
+
+
+static char *
+virFileNBDDeviceFindUnused(void)
+{
+    DIR *dh;
+    char *ret = NULL;
+    struct dirent *de;
+
+    if (!(dh = opendir(SYSFS_BLOCK_DIR))) {
+        virReportSystemError(errno,
+                             _("Cannot read directory %s"),
+                             SYSFS_BLOCK_DIR);
+        return NULL;
+    }
+
+    while ((de = readdir(dh)) != NULL) {
+        if (STRPREFIX(de->d_name, "nbd")) {
+            int rv = virFileNBDDeviceIsBusy(de->d_name);
+            if (rv < 0)
+                goto cleanup;
+            if (rv == 0) {
+                if (virAsprintf(&ret, "/dev/%s", de->d_name) < 0) {
+                    virReportOOMError();
+                    goto cleanup;
+                }
+                goto cleanup;
+            }
+        }
+    }
+
+    virReportSystemError(EBUSY, "%s",
+                         _("No free NBD devices"));
+
+cleanup:
+    closedir(dh);
+    return ret;
+}
+
+
+int virFileNBDDeviceAssociate(const char *file,
+                              enum virStorageFileFormat fmt,
+                              bool readonly,
+                              char **dev)
+{
+    char *nbddev;
+    char *qemunbd;
+    virCommandPtr cmd = NULL;
+    int ret = -1;
+    const char *fmtstr = NULL;
+
+    if (!(nbddev = virFileNBDDeviceFindUnused()))
+        goto cleanup;
+
+    if (!(qemunbd = virFindFileInPath("qemu-nbd"))) {
+        virReportSystemError(ENOENT, "%s",
+                             _("Unable to find 'qemu-nbd' binary in $PATH"));
+        goto cleanup;
+    }
+
+    if (fmt > 0)
+        fmtstr = virStorageFileFormatTypeToString(fmt);
+
+    cmd = virCommandNew(qemunbd);
+
+    /* Explicitly not trying to cope with old qemu-nbd which
+     * lacked --format. We want to see a fatal error in that
+     * case since it would be security flaw to continue */
+    if (fmtstr) {
+        virCommandAddArg(cmd, "--format");
+        virCommandAddArg(cmd, fmtstr);
+    }
+
+    if (readonly)
+        virCommandAddArg(cmd, "-r");
+
+    virCommandAddArgList(cmd,
+                         "-n", /* Don't cache in qemu-nbd layer */
+                         "-c", nbddev,
+                         file, NULL);
+
+    /* qemu-nbd will daemonize itself */
+
+    if (virCommandRun(cmd, NULL) < 0)
+        goto cleanup;
+
+    *dev = nbddev;
+    nbddev = NULL;
+    ret = 0;
+
+cleanup:
+    VIR_FREE(nbddev);
+    VIR_FREE(qemunbd);
+    virCommandFree(cmd);
+    return ret;
+}
+
 #else /* __linux__ */
 
 int virFileLoopDeviceAssociate(const char *file,
-                               char **dev ATTRIBUTE_UNUSED)
+                               char **dev ATTRIBUTE)
 {
     virReportSystemError(ENOSYS,
                          _("Unable to associate file %s with loop device"),
@@ -643,6 +777,17 @@ int virFileLoopDeviceAssociate(const char *file,
     return -1;
 }
 
+int virFileNBDDeviceAssociate(const char *file,
+                              enum virStorageFileFormat fmt ATTRIBUTE_UNUSED,
+                              bool readonly ATTRIBUTE_UNUSED,
+                              char **dev ATTRIBUTE_UNUSED)
+{
+    virReportSystemError(ENOSYS,
+                         _("Unable to associate file %s with NBD device"),
+                         file);
+    return -1;
+}
+
 #endif /* __linux__ */
 
 
diff --git a/src/util/virfile.h b/src/util/virfile.h
index 5f0dd2b..9bd8ea5 100644
--- a/src/util/virfile.h
+++ b/src/util/virfile.h
@@ -29,6 +29,7 @@
 # include <stdio.h>
 
 # include "internal.h"
+# include "virstoragefile.h"
 
 typedef enum virFileCloseFlags {
     VIR_FILE_CLOSE_PRESERVE_ERRNO = 1 << 0,
@@ -108,6 +109,11 @@ int virFileUpdatePerm(const char *path,
 int virFileLoopDeviceAssociate(const char *file,
                                char **dev);
 
+int virFileNBDDeviceAssociate(const char *file,
+                              enum virStorageFileFormat fmt,
+                              bool readonly,
+                              char **dev);
+
 int virFileDeleteTree(const char *dir);
 
 #endif /* __VIR_FILES_H */
-- 
1.7.11.7




More information about the libvir-list mailing list