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