From: "Daniel P. Berrange" <berrange(a)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(a)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