[libvirt] [PATCH 1/2] qemu: Split out various utility functions to qemu_util.c

Peter Krempa pkrempa at redhat.com
Thu Jan 3 16:45:49 UTC 2013


---
 po/POTFILES.in         |   1 +
 src/Makefile.am        |   1 +
 src/qemu/qemu_driver.c | 492 ++-----------------------------------------------
 src/qemu/qemu_util.c   | 486 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/qemu/qemu_util.h   | 111 +++++++++++
 5 files changed, 611 insertions(+), 480 deletions(-)
 create mode 100644 src/qemu/qemu_util.c
 create mode 100644 src/qemu/qemu_util.h

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 4d94799..4edacfa 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -101,6 +101,7 @@ src/qemu/qemu_monitor.c
 src/qemu/qemu_monitor_json.c
 src/qemu/qemu_monitor_text.c
 src/qemu/qemu_process.c
+src/qemu/qemu_util.c
 src/remote/remote_client_bodies.h
 src/remote/remote_driver.c
 src/rpc/virkeepalive.c
diff --git a/src/Makefile.am b/src/Makefile.am
index f7a9b91..f76a2ea 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -515,6 +515,7 @@ QEMU_DRIVER_SOURCES =							\
 		qemu/qemu_conf.c qemu/qemu_conf.h			\
 		qemu/qemu_process.c qemu/qemu_process.h			\
 		qemu/qemu_migration.c qemu/qemu_migration.h		\
+		qemu/qemu_util.c qemu/qemu_util.h		\
 		qemu/qemu_monitor.c qemu/qemu_monitor.h			\
 		qemu/qemu_monitor_text.c				\
 		qemu/qemu_monitor_text.h				\
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 3a52b47..a9c03b6 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -1,7 +1,7 @@
 /*
  * qemu_driver.c: core driver methods for managing qemu guests
  *
- * Copyright (C) 2006-2012 Red Hat, Inc.
+ * Copyright (C) 2006-2013 Red Hat, Inc.
  * Copyright (C) 2006 Daniel P. Berrange
  *
  * This library is free software; you can redistribute it and/or
@@ -57,6 +57,7 @@
 #include "qemu_bridge_filter.h"
 #include "qemu_process.h"
 #include "qemu_migration.h"
+#include "qemu_util.h"

 #include "virerror.h"
 #include "virlog.h"
@@ -186,97 +187,6 @@ struct qemuAutostartData {
     virConnectPtr conn;
 };

-/**
- * qemuDomObjFromDomainDriver:
- * @domain: Domain pointer that has to be looked up
- * @drv: Pointer to virQEMUDriverPtr to return the driver object
- *
- * This function looks up @domain in the domain list and returns the
- * appropriate virDomainObjPtr. On successful lookup, both driver and domain
- * object are returned locked.
- *
- * Returns the domain object if it's found and the driver. Both are locked.
- * In case of failure NULL is returned and the driver isn't locked.
- */
-static virDomainObjPtr
-qemuDomObjFromDomainDriver(virDomainPtr domain, virQEMUDriverPtr *drv)
-{
-    virQEMUDriverPtr driver = domain->conn->privateData;
-    virDomainObjPtr vm;
-    char uuidstr[VIR_UUID_STRING_BUFLEN];
-
-    qemuDriverLock(driver);
-    vm = virDomainFindByUUID(&driver->domains, domain->uuid);
-    if (!vm) {
-        virUUIDFormat(domain->uuid, uuidstr);
-        virReportError(VIR_ERR_NO_DOMAIN,
-                       _("no domain with matching uuid '%s'"), uuidstr);
-        qemuDriverUnlock(driver);
-        *drv = NULL;
-        return NULL;
-    }
-
-    *drv = driver;
-    return vm;
-}
-
-/**
- * qemuDomObjFromDomain:
- * @domain: Domain pointer that has to be looked up
- *
- * This function looks up @domain and returns the appropriate
- * virDomainObjPtr. The driver is unlocked after the call.
- *
- * Returns the domain object which is locked on success, NULL
- * otherwise. The driver remains unlocked after the call.
- */
-static virDomainObjPtr
-qemuDomObjFromDomain(virDomainPtr domain)
-{
-    virDomainObjPtr vm;
-    virQEMUDriverPtr driver;
-
-    if (!(vm = qemuDomObjFromDomainDriver(domain, &driver)))
-        return NULL;
-
-    qemuDriverUnlock(driver);
-
-    return vm;
-}
-
-/* Looks up the domain object from snapshot and unlocks the driver. The
- * returned domain object is locked and the caller is responsible for
- * unlocking it */
-static virDomainObjPtr
-qemuDomObjFromSnapshot(virDomainSnapshotPtr snapshot)
-{
-    return qemuDomObjFromDomain(snapshot->domain);
-}
-
-
-/* Looks up snapshot object from VM and name */
-static virDomainSnapshotObjPtr
-qemuSnapObjFromName(virDomainObjPtr vm,
-                    const char *name)
-{
-    virDomainSnapshotObjPtr snap = NULL;
-    snap = virDomainSnapshotFindByName(vm->snapshots, name);
-    if (!snap)
-        virReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT,
-                       _("no domain snapshot with matching name '%s'"),
-                       name);
-
-    return snap;
-}
-
-
-/* Looks up snapshot object from VM and snapshotPtr */
-static virDomainSnapshotObjPtr
-qemuSnapObjFromSnapshot(virDomainObjPtr vm,
-                        virDomainSnapshotPtr snapshot)
-{
-    return qemuSnapObjFromName(vm, snapshot->name);
-}

 static void
 qemuAutostartDomain(void *payload, const void *name ATTRIBUTE_UNUSED,
@@ -2583,336 +2493,8 @@ cleanup:
 }


-/* It would be nice to replace 'Qemud' with 'Qemu' but
- * this magic string is ABI, so it can't be changed
- */
-#define QEMU_SAVE_MAGIC   "LibvirtQemudSave"
-#define QEMU_SAVE_PARTIAL "LibvirtQemudPart"
-#define QEMU_SAVE_VERSION 2

-verify(sizeof(QEMU_SAVE_MAGIC) == sizeof(QEMU_SAVE_PARTIAL));

-typedef enum {
-    QEMU_SAVE_FORMAT_RAW = 0,
-    QEMU_SAVE_FORMAT_GZIP = 1,
-    QEMU_SAVE_FORMAT_BZIP2 = 2,
-    /*
-     * Deprecated by xz and never used as part of a release
-     * QEMU_SAVE_FORMAT_LZMA
-     */
-    QEMU_SAVE_FORMAT_XZ = 3,
-    QEMU_SAVE_FORMAT_LZOP = 4,
-    /* Note: add new members only at the end.
-       These values are used in the on-disk format.
-       Do not change or re-use numbers. */
-
-    QEMU_SAVE_FORMAT_LAST
-} virQEMUSaveFormat;
-
-VIR_ENUM_DECL(qemuSaveCompression)
-VIR_ENUM_IMPL(qemuSaveCompression, QEMU_SAVE_FORMAT_LAST,
-              "raw",
-              "gzip",
-              "bzip2",
-              "xz",
-              "lzop")
-
-typedef struct _virQEMUSaveHeader virQEMUSaveHeader;
-typedef virQEMUSaveHeader *virQEMUSaveHeaderPtr;
-struct _virQEMUSaveHeader {
-    char magic[sizeof(QEMU_SAVE_MAGIC)-1];
-    uint32_t version;
-    uint32_t xml_len;
-    uint32_t was_running;
-    uint32_t compressed;
-    uint32_t unused[15];
-};
-
-static inline void
-bswap_header(virQEMUSaveHeaderPtr hdr) {
-    hdr->version = bswap_32(hdr->version);
-    hdr->xml_len = bswap_32(hdr->xml_len);
-    hdr->was_running = bswap_32(hdr->was_running);
-    hdr->compressed = bswap_32(hdr->compressed);
-}
-
-
-/* return -errno on failure, or 0 on success */
-static int
-qemuDomainSaveHeader(int fd, const char *path, const char *xml,
-                     virQEMUSaveHeaderPtr header)
-{
-    int ret = 0;
-
-    if (safewrite(fd, header, sizeof(*header)) != sizeof(*header)) {
-        ret = -errno;
-        virReportError(VIR_ERR_OPERATION_FAILED,
-                       _("failed to write header to domain save file '%s'"),
-                       path);
-        goto endjob;
-    }
-
-    if (safewrite(fd, xml, header->xml_len) != header->xml_len) {
-        ret = -errno;
-        virReportError(VIR_ERR_OPERATION_FAILED,
-                       _("failed to write xml to '%s'"), path);
-        goto endjob;
-    }
-endjob:
-    return ret;
-}
-
-/* Given a virQEMUSaveFormat compression level, return the name
- * of the program to run, or NULL if no program is needed.  */
-static const char *
-qemuCompressProgramName(int compress)
-{
-    return (compress == QEMU_SAVE_FORMAT_RAW ? NULL :
-            qemuSaveCompressionTypeToString(compress));
-}
-
-/* Internal function to properly create or open existing files, with
- * ownership affected by qemu driver setup.  */
-static int
-qemuOpenFile(virQEMUDriverPtr driver, const char *path, int oflags,
-             bool *needUnlink, bool *bypassSecurityDriver)
-{
-    struct stat sb;
-    bool is_reg = true;
-    bool need_unlink = false;
-    bool bypass_security = false;
-    unsigned int vfoflags = 0;
-    int fd = -1;
-    int path_shared = virStorageFileIsSharedFS(path);
-    uid_t uid = getuid();
-    gid_t gid = getgid();
-
-    /* path might be a pre-existing block dev, in which case
-     * we need to skip the create step, and also avoid unlink
-     * in the failure case */
-    if (oflags & O_CREAT) {
-        need_unlink = true;
-
-        /* Don't force chown on network-shared FS
-         * as it is likely to fail. */
-        if (path_shared <= 0 || driver->dynamicOwnership)
-            vfoflags |= VIR_FILE_OPEN_FORCE_OWNER;
-
-        if (stat(path, &sb) == 0) {
-            is_reg = !!S_ISREG(sb.st_mode);
-            /* If the path is regular file which exists
-             * already and dynamic_ownership is off, we don't
-             * want to change it's ownership, just open it as-is */
-            if (is_reg && !driver->dynamicOwnership) {
-                uid = sb.st_uid;
-                gid = sb.st_gid;
-            }
-        }
-    }
-
-    /* First try creating the file as root */
-    if (!is_reg) {
-        fd = open(path, oflags & ~O_CREAT);
-        if (fd < 0) {
-            virReportSystemError(errno, _("unable to open %s"), path);
-            goto cleanup;
-        }
-    } else {
-        if ((fd = virFileOpenAs(path, oflags, S_IRUSR | S_IWUSR, uid, gid,
-                                vfoflags | VIR_FILE_OPEN_NOFORK)) < 0) {
-            /* If we failed as root, and the error was permission-denied
-               (EACCES or EPERM), assume it's on a network-connected share
-               where root access is restricted (eg, root-squashed NFS). If the
-               qemu user (driver->user) is non-root, just set a flag to
-               bypass security driver shenanigans, and retry the operation
-               after doing setuid to qemu user */
-            if ((fd != -EACCES && fd != -EPERM) ||
-                driver->user == getuid()) {
-                virReportSystemError(-fd,
-                                     _("Failed to create file '%s'"),
-                                     path);
-                goto cleanup;
-            }
-
-            /* On Linux we can also verify the FS-type of the directory. */
-            switch (path_shared) {
-                case 1:
-                   /* it was on a network share, so we'll continue
-                    * as outlined above
-                    */
-                   break;
-
-                case -1:
-                   virReportSystemError(errno,
-                                        _("Failed to create file "
-                                          "'%s': couldn't determine fs type"),
-                                        path);
-                   goto cleanup;
-
-                case 0:
-                default:
-                   /* local file - log the error returned by virFileOpenAs */
-                   virReportSystemError(-fd,
-                                        _("Failed to create file '%s'"),
-                                        path);
-                   goto cleanup;
-            }
-
-            /* Retry creating the file as driver->user */
-
-            if ((fd = virFileOpenAs(path, oflags,
-                                    S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP,
-                                    driver->user, driver->group,
-                                    vfoflags | VIR_FILE_OPEN_FORK)) < 0) {
-                virReportSystemError(-fd,
-                                   _("Error from child process creating '%s'"),
-                                     path);
-                goto cleanup;
-            }
-
-            /* Since we had to setuid to create the file, and the fstype
-               is NFS, we assume it's a root-squashing NFS share, and that
-               the security driver stuff would have failed anyway */
-
-            bypass_security = true;
-        }
-    }
-cleanup:
-    if (needUnlink)
-        *needUnlink = need_unlink;
-    if (bypassSecurityDriver)
-        *bypassSecurityDriver = bypass_security;
-
-    return fd;
-}
-
-/* Helper function to execute a migration to file with a correct save header
- * the caller needs to make sure that the processors are stopped and do all other
- * actions besides saving memory */
-static int
-qemuDomainSaveMemory(virQEMUDriverPtr driver,
-                     virDomainObjPtr vm,
-                     const char *path,
-                     const char *domXML,
-                     int compressed,
-                     bool was_running,
-                     unsigned int flags,
-                     enum qemuDomainAsyncJob asyncJob)
-{
-    virQEMUSaveHeader header;
-    bool bypassSecurityDriver = false;
-    bool needUnlink = false;
-    int ret = -1;
-    int fd = -1;
-    int directFlag = 0;
-    virFileWrapperFdPtr wrapperFd = NULL;
-    unsigned int wrapperFlags = VIR_FILE_WRAPPER_NON_BLOCKING;
-    unsigned long long pad;
-    unsigned long long offset;
-    size_t len;
-    char *xml = NULL;
-
-    memset(&header, 0, sizeof(header));
-    memcpy(header.magic, QEMU_SAVE_PARTIAL, sizeof(header.magic));
-    header.version = QEMU_SAVE_VERSION;
-    header.was_running = was_running ? 1 : 0;
-
-    header.compressed = compressed;
-
-    len = strlen(domXML) + 1;
-    offset = sizeof(header) + len;
-
-    /* Due to way we append QEMU state on our header with dd,
-     * we need to ensure there's a 512 byte boundary. Unfortunately
-     * we don't have an explicit offset in the header, so we fake
-     * it by padding the XML string with NUL bytes.  Additionally,
-     * we want to ensure that virDomainSaveImageDefineXML can supply
-     * slightly larger XML, so we add a miminum padding prior to
-     * rounding out to page boundaries.
-     */
-    pad = 1024;
-    pad += (QEMU_MONITOR_MIGRATE_TO_FILE_BS -
-            ((offset + pad) % QEMU_MONITOR_MIGRATE_TO_FILE_BS));
-    if (VIR_ALLOC_N(xml, len + pad) < 0) {
-        virReportOOMError();
-        goto cleanup;
-    }
-    strcpy(xml, domXML);
-
-    offset += pad;
-    header.xml_len = len;
-
-    /* Obtain the file handle.  */
-    if ((flags & VIR_DOMAIN_SAVE_BYPASS_CACHE)) {
-        wrapperFlags |= VIR_FILE_WRAPPER_BYPASS_CACHE;
-        directFlag = virFileDirectFdFlag();
-        if (directFlag < 0) {
-            virReportError(VIR_ERR_OPERATION_FAILED, "%s",
-                           _("bypass cache unsupported by this system"));
-            goto cleanup;
-        }
-    }
-    fd = qemuOpenFile(driver, path, O_WRONLY | O_TRUNC | O_CREAT | directFlag,
-                      &needUnlink, &bypassSecurityDriver);
-    if (fd < 0)
-        goto cleanup;
-
-    if (!(wrapperFd = virFileWrapperFdNew(&fd, path, wrapperFlags)))
-        goto cleanup;
-
-    /* Write header to file, followed by XML */
-    if (qemuDomainSaveHeader(fd, path, xml, &header) < 0)
-        goto cleanup;
-
-    /* Perform the migration */
-    if (qemuMigrationToFile(driver, vm, fd, offset, path,
-                            qemuCompressProgramName(compressed),
-                            bypassSecurityDriver,
-                            asyncJob) < 0)
-        goto cleanup;
-
-    /* Touch up file header to mark image complete. */
-
-    /* Reopen the file to touch up the header, since we aren't set
-     * up to seek backwards on wrapperFd.  The reopened fd will
-     * trigger a single page of file system cache pollution, but
-     * that's acceptable.  */
-    if (VIR_CLOSE(fd) < 0) {
-        virReportSystemError(errno, _("unable to close %s"), path);
-        goto cleanup;
-    }
-
-    if (virFileWrapperFdClose(wrapperFd) < 0)
-        goto cleanup;
-
-    if ((fd = qemuOpenFile(driver, path, O_WRONLY, NULL, NULL)) < 0)
-        goto cleanup;
-
-    memcpy(header.magic, QEMU_SAVE_MAGIC, sizeof(header.magic));
-
-    if (safewrite(fd, &header, sizeof(header)) != sizeof(header)) {
-        virReportSystemError(errno, _("unable to write %s"), path);
-        goto cleanup;
-    }
-
-    if (VIR_CLOSE(fd) < 0) {
-        virReportSystemError(errno, _("unable to close %s"), path);
-        goto cleanup;
-    }
-
-    ret = 0;
-
-cleanup:
-    VIR_FORCE_CLOSE(fd);
-    virFileWrapperFdCatchError(wrapperFd);
-    virFileWrapperFdFree(wrapperFd);
-    VIR_FREE(xml);
-
-    if (ret != 0 && needUnlink)
-        unlink(path);
-
-    return ret;
-}

 /* This internal function expects the driver lock to already be held on
  * entry and the vm must be active + locked. Vm will be unlocked and
@@ -4703,6 +4285,16 @@ cleanup:
     return ret;
 }

+
+static inline void
+bswap_header(virQEMUSaveHeaderPtr hdr) {
+    hdr->version = bswap_32(hdr->version);
+    hdr->xml_len = bswap_32(hdr->xml_len);
+    hdr->was_running = bswap_32(hdr->was_running);
+    hdr->compressed = bswap_32(hdr->compressed);
+}
+
+
 /* Return -1 on most failures after raising error, -2 if edit was specified
  * but xmlin and state (-1 for no change, 0 for paused, 1 for running) do
  * not represent any changes (no error raised), -3 if corrupt image was
@@ -10329,66 +9921,6 @@ cleanup:
     return ret;
 }

-typedef enum {
-    VIR_DISK_CHAIN_NO_ACCESS,
-    VIR_DISK_CHAIN_READ_ONLY,
-    VIR_DISK_CHAIN_READ_WRITE,
-} qemuDomainDiskChainMode;
-
-/* Several operations end up adding or removing a single element of a
- * disk backing file chain; this helper function ensures that the lock
- * manager, cgroup device controller, and security manager labelling
- * are all aware of each new file before it is added to a chain, and
- * can revoke access to a file no longer needed in a chain.  */
-static int
-qemuDomainPrepareDiskChainElement(virQEMUDriverPtr driver,
-                                  virDomainObjPtr vm,
-                                  virCgroupPtr cgroup,
-                                  virDomainDiskDefPtr disk,
-                                  const char *file,
-                                  qemuDomainDiskChainMode mode)
-{
-    /* The easiest way to label a single file with the same
-     * permissions it would have as if part of the disk chain is to
-     * temporarily modify the disk in place.  */
-    char *origsrc = disk->src;
-    int origformat = disk->format;
-    virStorageFileMetadataPtr origchain = disk->backingChain;
-    bool origreadonly = disk->readonly;
-    int ret = -1;
-
-    disk->src = (char *) file; /* casting away const is safe here */
-    disk->format = VIR_STORAGE_FILE_RAW;
-    disk->backingChain = NULL;
-    disk->readonly = mode == VIR_DISK_CHAIN_READ_ONLY;
-
-    if (mode == VIR_DISK_CHAIN_NO_ACCESS) {
-        if (virSecurityManagerRestoreImageLabel(driver->securityManager,
-                                                vm->def, disk) < 0)
-            VIR_WARN("Unable to restore security label on %s", disk->src);
-        if (cgroup && qemuTeardownDiskCgroup(vm, cgroup, disk) < 0)
-            VIR_WARN("Failed to teardown cgroup for disk path %s", disk->src);
-        if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0)
-            VIR_WARN("Unable to release lock on %s", disk->src);
-    } else if (virDomainLockDiskAttach(driver->lockManager, driver->uri,
-                                       vm, disk) < 0 ||
-               (cgroup && qemuSetupDiskCgroup(vm, cgroup, disk) < 0) ||
-               virSecurityManagerSetImageLabel(driver->securityManager,
-                                               vm->def, disk) < 0) {
-        goto cleanup;
-    }
-
-    ret = 0;
-
-cleanup:
-    disk->src = origsrc;
-    disk->format = origformat;
-    disk->backingChain = origchain;
-    disk->readonly = origreadonly;
-    return ret;
-}
-
-

 /* this function expects the driver lock to be held by the caller */
 static int
diff --git a/src/qemu/qemu_util.c b/src/qemu/qemu_util.c
new file mode 100644
index 0000000..bc958fc
--- /dev/null
+++ b/src/qemu/qemu_util.c
@@ -0,0 +1,486 @@
+/*
+ * qemu_util.c: Various util functions for the QEMU driver
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <fcntl.h>
+
+#include "qemu_util.h"
+#include "qemu_cgroup.h"
+#include "qemu_migration.h"
+
+#include "internal.h"
+
+#include "virerror.h"
+#include "domain_conf.h"
+#include "locking/domain_lock.h"
+#include "datatypes.h"
+#include "virlog.h"
+#include "viralloc.h"
+#include "virfile.h"
+
+#define VIR_FROM_THIS VIR_FROM_QEMU
+
+VIR_ENUM_IMPL(qemuSaveCompression, QEMU_SAVE_FORMAT_LAST,
+              "raw",
+              "gzip",
+              "bzip2",
+              "xz",
+              "lzop")
+
+/**
+ * qemuDomObjFromDomainDriver:
+ * @domain: Domain pointer that has to be looked up
+ * @drv: Pointer to virQEMUDriverPtr to return the driver object
+ *
+ * This function looks up @domain in the domain list and returns the
+ * appropriate virDomainObjPtr. On successful lookup, both driver and domain
+ * object are returned locked.
+ *
+ * Returns the domain object if it's found and the driver. Both are locked.
+ * In case of failure NULL is returned and the driver isn't locked.
+ */
+virDomainObjPtr
+qemuDomObjFromDomainDriver(virDomainPtr domain,
+                           virQEMUDriverPtr *drv)
+{
+    virQEMUDriverPtr driver = domain->conn->privateData;
+    virDomainObjPtr vm;
+    char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+    qemuDriverLock(driver);
+    vm = virDomainFindByUUID(&driver->domains, domain->uuid);
+    if (!vm) {
+        virUUIDFormat(domain->uuid, uuidstr);
+        virReportError(VIR_ERR_NO_DOMAIN,
+                       _("no domain with matching uuid '%s'"), uuidstr);
+        qemuDriverUnlock(driver);
+        *drv = NULL;
+        return NULL;
+    }
+
+    *drv = driver;
+    return vm;
+}
+
+
+/**
+ * qemuDomObjFromDomain:
+ * @domain: Domain pointer that has to be looked up
+ *
+ * This function looks up @domain and returns the appropriate
+ * virDomainObjPtr. The driver is unlocked after the call.
+ *
+ * Returns the domain object which is locked on success, NULL
+ * otherwise. The driver remains unlocked after the call.
+ */
+virDomainObjPtr
+qemuDomObjFromDomain(virDomainPtr domain)
+{
+    virDomainObjPtr vm;
+    virQEMUDriverPtr driver;
+
+    if (!(vm = qemuDomObjFromDomainDriver(domain, &driver)))
+        return NULL;
+
+    qemuDriverUnlock(driver);
+
+    return vm;
+}
+
+
+/* Looks up the domain object from snapshot and unlocks the driver. The
+ * returned domain object is locked and the caller is responsible for
+ * unlocking it */
+virDomainObjPtr
+qemuDomObjFromSnapshot(virDomainSnapshotPtr snapshot)
+{
+    return qemuDomObjFromDomain(snapshot->domain);
+}
+
+
+/* Looks up snapshot object from VM and name */
+virDomainSnapshotObjPtr
+qemuSnapObjFromName(virDomainObjPtr vm,
+                    const char *name)
+{
+    virDomainSnapshotObjPtr snap = NULL;
+    snap = virDomainSnapshotFindByName(vm->snapshots, name);
+    if (!snap)
+        virReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT,
+                       _("no domain snapshot with matching name '%s'"),
+                       name);
+
+    return snap;
+}
+
+
+/* Looks up snapshot object from VM and snapshotPtr */
+virDomainSnapshotObjPtr
+qemuSnapObjFromSnapshot(virDomainObjPtr vm,
+                        virDomainSnapshotPtr snapshot)
+{
+    return qemuSnapObjFromName(vm, snapshot->name);
+}
+
+
+/* Internal function to properly create or open existing files, with
+ * ownership affected by qemu driver setup.  */
+int
+qemuOpenFile(virQEMUDriverPtr driver,
+             const char *path,
+             int oflags,
+             bool *needUnlink,
+             bool *bypassSecurityDriver)
+{
+    struct stat sb;
+    bool is_reg = true;
+    bool need_unlink = false;
+    bool bypass_security = false;
+    unsigned int vfoflags = 0;
+    int fd = -1;
+    int path_shared = virStorageFileIsSharedFS(path);
+    uid_t uid = getuid();
+    gid_t gid = getgid();
+
+    /* path might be a pre-existing block dev, in which case
+     * we need to skip the create step, and also avoid unlink
+     * in the failure case */
+    if (oflags & O_CREAT) {
+        need_unlink = true;
+
+        /* Don't force chown on network-shared FS
+         * as it is likely to fail. */
+        if (path_shared <= 0 || driver->dynamicOwnership)
+            vfoflags |= VIR_FILE_OPEN_FORCE_OWNER;
+
+        if (stat(path, &sb) == 0) {
+            is_reg = !!S_ISREG(sb.st_mode);
+            /* If the path is regular file which exists
+             * already and dynamic_ownership is off, we don't
+             * want to change it's ownership, just open it as-is */
+            if (is_reg && !driver->dynamicOwnership) {
+                uid = sb.st_uid;
+                gid = sb.st_gid;
+            }
+        }
+    }
+
+    /* First try creating the file as root */
+    if (!is_reg) {
+        fd = open(path, oflags & ~O_CREAT);
+        if (fd < 0) {
+            virReportSystemError(errno, _("unable to open %s"), path);
+            goto cleanup;
+        }
+    } else {
+        if ((fd = virFileOpenAs(path, oflags, S_IRUSR | S_IWUSR, uid, gid,
+                                vfoflags | VIR_FILE_OPEN_NOFORK)) < 0) {
+            /* If we failed as root, and the error was permission-denied
+               (EACCES or EPERM), assume it's on a network-connected share
+               where root access is restricted (eg, root-squashed NFS). If the
+               qemu user (driver->user) is non-root, just set a flag to
+               bypass security driver shenanigans, and retry the operation
+               after doing setuid to qemu user */
+            if ((fd != -EACCES && fd != -EPERM) ||
+                driver->user == getuid()) {
+                virReportSystemError(-fd,
+                                     _("Failed to create file '%s'"),
+                                     path);
+                goto cleanup;
+            }
+
+            /* On Linux we can also verify the FS-type of the directory. */
+            switch (path_shared) {
+                case 1:
+                   /* it was on a network share, so we'll continue
+                    * as outlined above
+                    */
+                   break;
+
+                case -1:
+                   virReportSystemError(errno,
+                                        _("Failed to create file "
+                                          "'%s': couldn't determine fs type"),
+                                        path);
+                   goto cleanup;
+
+                case 0:
+                default:
+                   /* local file - log the error returned by virFileOpenAs */
+                   virReportSystemError(-fd,
+                                        _("Failed to create file '%s'"),
+                                        path);
+                   goto cleanup;
+            }
+
+            /* Retry creating the file as driver->user */
+
+            if ((fd = virFileOpenAs(path, oflags,
+                                    S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP,
+                                    driver->user, driver->group,
+                                    vfoflags | VIR_FILE_OPEN_FORK)) < 0) {
+                virReportSystemError(-fd,
+                                   _("Error from child process creating '%s'"),
+                                     path);
+                goto cleanup;
+            }
+
+            /* Since we had to setuid to create the file, and the fstype
+               is NFS, we assume it's a root-squashing NFS share, and that
+               the security driver stuff would have failed anyway */
+
+            bypass_security = true;
+        }
+    }
+cleanup:
+    if (needUnlink)
+        *needUnlink = need_unlink;
+    if (bypassSecurityDriver)
+        *bypassSecurityDriver = bypass_security;
+
+    return fd;
+}
+
+
+/* Several operations end up adding or removing a single element of a
+ * disk backing file chain; this helper function ensures that the lock
+ * manager, cgroup device controller, and security manager labelling
+ * are all aware of each new file before it is added to a chain, and
+ * can revoke access to a file no longer needed in a chain.  */
+int
+qemuDomainPrepareDiskChainElement(virQEMUDriverPtr driver,
+                                  virDomainObjPtr vm,
+                                  virCgroupPtr cgroup,
+                                  virDomainDiskDefPtr disk,
+                                  const char *file,
+                                  qemuDomainDiskChainMode mode)
+{
+    /* The easiest way to label a single file with the same
+     * permissions it would have as if part of the disk chain is to
+     * temporarily modify the disk in place.  */
+    char *origsrc = disk->src;
+    int origformat = disk->format;
+    virStorageFileMetadataPtr origchain = disk->backingChain;
+    bool origreadonly = disk->readonly;
+    int ret = -1;
+
+    disk->src = (char *) file; /* casting away const is safe here */
+    disk->format = VIR_STORAGE_FILE_RAW;
+    disk->backingChain = NULL;
+    disk->readonly = mode == VIR_DISK_CHAIN_READ_ONLY;
+
+    if (mode == VIR_DISK_CHAIN_NO_ACCESS) {
+        if (virSecurityManagerRestoreImageLabel(driver->securityManager,
+                                                vm->def, disk) < 0)
+            VIR_WARN("Unable to restore security label on %s", disk->src);
+        if (cgroup && qemuTeardownDiskCgroup(vm, cgroup, disk) < 0)
+            VIR_WARN("Failed to teardown cgroup for disk path %s", disk->src);
+        if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0)
+            VIR_WARN("Unable to release lock on %s", disk->src);
+    } else if (virDomainLockDiskAttach(driver->lockManager, driver->uri,
+                                       vm, disk) < 0 ||
+               (cgroup && qemuSetupDiskCgroup(vm, cgroup, disk) < 0) ||
+               virSecurityManagerSetImageLabel(driver->securityManager,
+                                               vm->def, disk) < 0) {
+        goto cleanup;
+    }
+
+    ret = 0;
+
+cleanup:
+    disk->src = origsrc;
+    disk->format = origformat;
+    disk->backingChain = origchain;
+    disk->readonly = origreadonly;
+    return ret;
+}
+
+
+/* return -errno on failure, or 0 on success */
+static int
+qemuDomainSaveHeader(int fd,
+                     const char *path,
+                     const char *xml,
+                     virQEMUSaveHeaderPtr header)
+{
+    int ret = 0;
+
+    if (safewrite(fd, header, sizeof(*header)) != sizeof(*header)) {
+        ret = -errno;
+        virReportError(VIR_ERR_OPERATION_FAILED,
+                       _("failed to write header to domain save file '%s'"),
+                       path);
+        goto endjob;
+    }
+
+    if (safewrite(fd, xml, header->xml_len) != header->xml_len) {
+        ret = -errno;
+        virReportError(VIR_ERR_OPERATION_FAILED,
+                       _("failed to write xml to '%s'"), path);
+        goto endjob;
+    }
+endjob:
+    return ret;
+}
+
+
+/* Given a virQEMUSaveFormat compression level, return the name
+ * of the program to run, or NULL if no program is needed.  */
+const char *
+qemuCompressProgramName(int compress)
+{
+    return (compress == QEMU_SAVE_FORMAT_RAW ? NULL :
+            qemuSaveCompressionTypeToString(compress));
+}
+
+
+/* Helper function to execute a migration to file with a correct save header
+ * the caller needs to make sure that the processors are stopped and do all other
+ * actions besides saving memory */
+int
+qemuDomainSaveMemory(virQEMUDriverPtr driver,
+                     virDomainObjPtr vm,
+                     const char *path,
+                     const char *domXML,
+                     int compressed,
+                     bool was_running,
+                     unsigned int flags,
+                     enum qemuDomainAsyncJob asyncJob)
+{
+    virQEMUSaveHeader header;
+    bool bypassSecurityDriver = false;
+    bool needUnlink = false;
+    int ret = -1;
+    int fd = -1;
+    int directFlag = 0;
+    virFileWrapperFdPtr wrapperFd = NULL;
+    unsigned int wrapperFlags = VIR_FILE_WRAPPER_NON_BLOCKING;
+    unsigned long long pad;
+    unsigned long long offset;
+    size_t len;
+    char *xml = NULL;
+
+    memset(&header, 0, sizeof(header));
+    memcpy(header.magic, QEMU_SAVE_PARTIAL, sizeof(header.magic));
+    header.version = QEMU_SAVE_VERSION;
+    header.was_running = was_running ? 1 : 0;
+
+    header.compressed = compressed;
+
+    len = strlen(domXML) + 1;
+    offset = sizeof(header) + len;
+
+    /* Due to way we append QEMU state on our header with dd,
+     * we need to ensure there's a 512 byte boundary. Unfortunately
+     * we don't have an explicit offset in the header, so we fake
+     * it by padding the XML string with NUL bytes.  Additionally,
+     * we want to ensure that virDomainSaveImageDefineXML can supply
+     * slightly larger XML, so we add a miminum padding prior to
+     * rounding out to page boundaries.
+     */
+    pad = 1024;
+    pad += (QEMU_MONITOR_MIGRATE_TO_FILE_BS -
+            ((offset + pad) % QEMU_MONITOR_MIGRATE_TO_FILE_BS));
+    if (VIR_ALLOC_N(xml, len + pad) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+    strcpy(xml, domXML);
+
+    offset += pad;
+    header.xml_len = len;
+
+    /* Obtain the file handle.  */
+    if ((flags & VIR_DOMAIN_SAVE_BYPASS_CACHE)) {
+        wrapperFlags |= VIR_FILE_WRAPPER_BYPASS_CACHE;
+        directFlag = virFileDirectFdFlag();
+        if (directFlag < 0) {
+            virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+                           _("bypass cache unsupported by this system"));
+            goto cleanup;
+        }
+    }
+    fd = qemuOpenFile(driver, path, O_WRONLY | O_TRUNC | O_CREAT | directFlag,
+                      &needUnlink, &bypassSecurityDriver);
+    if (fd < 0)
+        goto cleanup;
+
+    if (!(wrapperFd = virFileWrapperFdNew(&fd, path, wrapperFlags)))
+        goto cleanup;
+
+    /* Write header to file, followed by XML */
+    if (qemuDomainSaveHeader(fd, path, xml, &header) < 0)
+        goto cleanup;
+
+    /* Perform the migration */
+    if (qemuMigrationToFile(driver, vm, fd, offset, path,
+                            qemuCompressProgramName(compressed),
+                            bypassSecurityDriver,
+                            asyncJob) < 0)
+        goto cleanup;
+
+    /* Touch up file header to mark image complete. */
+
+    /* Reopen the file to touch up the header, since we aren't set
+     * up to seek backwards on wrapperFd.  The reopened fd will
+     * trigger a single page of file system cache pollution, but
+     * that's acceptable.  */
+    if (VIR_CLOSE(fd) < 0) {
+        virReportSystemError(errno, _("unable to close %s"), path);
+        goto cleanup;
+    }
+
+    if (virFileWrapperFdClose(wrapperFd) < 0)
+        goto cleanup;
+
+    if ((fd = qemuOpenFile(driver, path, O_WRONLY, NULL, NULL)) < 0)
+        goto cleanup;
+
+    memcpy(header.magic, QEMU_SAVE_MAGIC, sizeof(header.magic));
+
+    if (safewrite(fd, &header, sizeof(header)) != sizeof(header)) {
+        virReportSystemError(errno, _("unable to write %s"), path);
+        goto cleanup;
+    }
+
+    if (VIR_CLOSE(fd) < 0) {
+        virReportSystemError(errno, _("unable to close %s"), path);
+        goto cleanup;
+    }
+
+    ret = 0;
+
+cleanup:
+    VIR_FORCE_CLOSE(fd);
+    virFileWrapperFdCatchError(wrapperFd);
+    virFileWrapperFdFree(wrapperFd);
+    VIR_FREE(xml);
+
+    if (ret != 0 && needUnlink)
+        unlink(path);
+
+    return ret;
+}
diff --git a/src/qemu/qemu_util.h b/src/qemu/qemu_util.h
new file mode 100644
index 0000000..458af38
--- /dev/null
+++ b/src/qemu/qemu_util.h
@@ -0,0 +1,111 @@
+/*
+ * qemu_util.h: Various util functions for the QEMU driver
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __QEMU_UTIL_H__
+# define __QEMU_UTIL_H__
+
+# include "qemu_domain.h"
+
+/* It would be nice to replace 'Qemud' with 'Qemu' but
+ * this magic string is ABI, so it can't be changed
+ */
+# define QEMU_SAVE_MAGIC   "LibvirtQemudSave"
+# define QEMU_SAVE_PARTIAL "LibvirtQemudPart"
+# define QEMU_SAVE_VERSION 2
+
+verify(sizeof(QEMU_SAVE_MAGIC) == sizeof(QEMU_SAVE_PARTIAL));
+
+typedef struct _virQEMUSaveHeader virQEMUSaveHeader;
+typedef virQEMUSaveHeader *virQEMUSaveHeaderPtr;
+struct _virQEMUSaveHeader {
+    char magic[sizeof(QEMU_SAVE_MAGIC)-1];
+    uint32_t version;
+    uint32_t xml_len;
+    uint32_t was_running;
+    uint32_t compressed;
+    uint32_t unused[15];
+};
+
+
+typedef enum {
+    VIR_DISK_CHAIN_NO_ACCESS,
+    VIR_DISK_CHAIN_READ_ONLY,
+    VIR_DISK_CHAIN_READ_WRITE,
+} qemuDomainDiskChainMode;
+
+typedef enum {
+    QEMU_SAVE_FORMAT_RAW = 0,
+    QEMU_SAVE_FORMAT_GZIP = 1,
+    QEMU_SAVE_FORMAT_BZIP2 = 2,
+    /*
+     * Deprecated by xz and never used as part of a release
+     * QEMU_SAVE_FORMAT_LZMA
+     */
+    QEMU_SAVE_FORMAT_XZ = 3,
+    QEMU_SAVE_FORMAT_LZOP = 4,
+    /* Note: add new members only at the end.
+       These values are used in the on-disk format.
+       Do not change or re-use numbers. */
+
+    QEMU_SAVE_FORMAT_LAST
+} virQEMUSaveFormat;
+
+VIR_ENUM_DECL(qemuSaveCompression)
+
+
+virDomainObjPtr qemuDomObjFromDomainDriver(virDomainPtr domain,
+                                           virQEMUDriverPtr *drv);
+
+virDomainObjPtr qemuDomObjFromDomain(virDomainPtr domain);
+
+virDomainObjPtr qemuDomObjFromSnapshot(virDomainSnapshotPtr snapshot);
+
+virDomainSnapshotObjPtr qemuSnapObjFromSnapshot(virDomainObjPtr vm,
+                                                virDomainSnapshotPtr snapshot);
+
+virDomainSnapshotObjPtr qemuSnapObjFromName(virDomainObjPtr vm,
+                                            const char *name);
+
+int qemuOpenFile(virQEMUDriverPtr driver,
+                 const char *path,
+                 int oflags,
+                 bool *needUnlink,
+                 bool *bypassSecurityDriver);
+
+int qemuDomainPrepareDiskChainElement(virQEMUDriverPtr driver,
+                                      virDomainObjPtr vm,
+                                      virCgroupPtr cgroup,
+                                      virDomainDiskDefPtr disk,
+                                      const char *file,
+                                      qemuDomainDiskChainMode mode);
+
+int qemuDomainSaveMemory(virQEMUDriverPtr driver,
+                         virDomainObjPtr vm,
+                         const char *path,
+                         const char *domXML,
+                         int compressed,
+                         bool was_running,
+                         unsigned int flags,
+                         enum qemuDomainAsyncJob asyncJob);
+
+const char *qemuCompressProgramName(int compress);
+
+#endif /* __QEMU_UTIL_H__ */
-- 
1.8.0.2




More information about the libvir-list mailing list