blockcopy: tweak how rebase calls into copy

In order to implement the new virDomainBlockCopy, the existing
block copy internal implementation needs to be adjusted.  The
new function will parse XML into a storage source, and parse
typed parameters into integers, then call into the same common
backend.  For now, it's easier to keep the same implementation
limits that only local file destinations are suported, but now
the check needs to be explicit.  Similar to qemuDomainBlockJobImpl
consuming 'vm', this code also consumes the caller's 'mirror'
description of the destination.

* src/qemu/qemu_driver.c (qemuDomainBlockCopy): Rename...
(qemuDomainBlockCopyCommon): ...and adjust parameters.
(qemuDomainBlockRebase): Adjust caller.

Signed-off-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
Eric Blake 2014-08-29 16:30:46 -06:00
parent dcf7d0423c
commit e53ee6c123

View File

@ -14934,7 +14934,8 @@ qemuDomainBlockPivot(virConnectPtr conn,
} }
/* bandwidth in MiB/s per public API */ /* bandwidth in MiB/s per public API. Caller must lock vm beforehand,
* and not access it afterwards. */
static int static int
qemuDomainBlockJobImpl(virDomainObjPtr vm, qemuDomainBlockJobImpl(virDomainObjPtr vm,
virConnectPtr conn, virConnectPtr conn,
@ -15295,13 +15296,16 @@ qemuDomainBlockJobSetSpeed(virDomainPtr dom, const char *path,
} }
/* bandwidth in bytes/s */ /* bandwidth in bytes/s. Caller must lock vm beforehand, and not
* access it afterwards; likewise, caller must not access mirror
* afterwards. */
static int static int
qemuDomainBlockCopy(virDomainObjPtr vm, qemuDomainBlockCopyCommon(virDomainObjPtr vm,
virConnectPtr conn, virConnectPtr conn,
const char *path, const char *path,
const char *dest, const char *format, virStorageSourcePtr mirror,
unsigned long long bandwidth, unsigned int flags) unsigned long long bandwidth,
unsigned int flags)
{ {
virQEMUDriverPtr driver = conn->privateData; virQEMUDriverPtr driver = conn->privateData;
qemuDomainObjPrivatePtr priv; qemuDomainObjPrivatePtr priv;
@ -15311,13 +15315,13 @@ qemuDomainBlockCopy(virDomainObjPtr vm,
int idx; int idx;
struct stat st; struct stat st;
bool need_unlink = false; bool need_unlink = false;
virStorageSourcePtr mirror = NULL;
virQEMUDriverConfigPtr cfg = NULL; virQEMUDriverConfigPtr cfg = NULL;
const char *format = NULL;
int desttype = virStorageSourceGetActualType(mirror);
/* Preliminaries: find the disk we are editing, sanity checks */ /* Preliminaries: find the disk we are editing, sanity checks */
virCheckFlags(VIR_DOMAIN_BLOCK_REBASE_SHALLOW | virCheckFlags(VIR_DOMAIN_BLOCK_COPY_SHALLOW |
VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT | VIR_DOMAIN_BLOCK_COPY_REUSE_EXT, -1);
VIR_DOMAIN_BLOCK_REBASE_COPY_DEV, -1);
priv = vm->privateData; priv = vm->privateData;
cfg = virQEMUDriverGetConfig(driver); cfg = virQEMUDriverGetConfig(driver);
@ -15362,8 +15366,8 @@ qemuDomainBlockCopy(virDomainObjPtr vm,
if (qemuDomainDetermineDiskChain(driver, vm, disk, false) < 0) if (qemuDomainDetermineDiskChain(driver, vm, disk, false) < 0)
goto endjob; goto endjob;
if ((flags & VIR_DOMAIN_BLOCK_REBASE_SHALLOW) && if ((flags & VIR_DOMAIN_BLOCK_COPY_SHALLOW) &&
STREQ_NULLABLE(format, "raw") && mirror->format == VIR_STORAGE_FILE_RAW &&
disk->src->backingStore->path) { disk->src->backingStore->path) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("disk '%s' has backing file, so raw shallow copy " _("disk '%s' has backing file, so raw shallow copy "
@ -15373,72 +15377,65 @@ qemuDomainBlockCopy(virDomainObjPtr vm,
} }
/* Prepare the destination file. */ /* Prepare the destination file. */
if (stat(dest, &st) < 0) { /* XXX Allow non-file mirror destinations */
if (!virStorageSourceIsLocalStorage(mirror)) {
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
_("non-file destination not supported yet"));
}
if (stat(mirror->path, &st) < 0) {
if (errno != ENOENT) { if (errno != ENOENT) {
virReportSystemError(errno, _("unable to stat for disk %s: %s"), virReportSystemError(errno, _("unable to stat for disk %s: %s"),
disk->dst, dest); disk->dst, mirror->path);
goto endjob; goto endjob;
} else if (flags & (VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT | } else if (flags & VIR_DOMAIN_BLOCK_COPY_REUSE_EXT ||
VIR_DOMAIN_BLOCK_REBASE_COPY_DEV)) { desttype == VIR_STORAGE_TYPE_BLOCK) {
virReportSystemError(errno, virReportSystemError(errno,
_("missing destination file for disk %s: %s"), _("missing destination file for disk %s: %s"),
disk->dst, dest); disk->dst, mirror->path);
goto endjob; goto endjob;
} }
} else if (!S_ISBLK(st.st_mode)) { } else if (!S_ISBLK(st.st_mode)) {
if (st.st_size && !(flags & VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT)) { if (st.st_size && !(flags & VIR_DOMAIN_BLOCK_COPY_REUSE_EXT)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("external destination file for disk %s already " _("external destination file for disk %s already "
"exists and is not a block device: %s"), "exists and is not a block device: %s"),
disk->dst, dest); disk->dst, mirror->path);
goto endjob; goto endjob;
} }
if (flags & VIR_DOMAIN_BLOCK_REBASE_COPY_DEV) { if (desttype == VIR_STORAGE_TYPE_BLOCK) {
virReportError(VIR_ERR_INVALID_ARG, virReportError(VIR_ERR_INVALID_ARG,
_("blockdev flag requested for disk %s, but file " _("blockdev flag requested for disk %s, but file "
"'%s' is not a block device"), disk->dst, dest); "'%s' is not a block device"),
disk->dst, mirror->path);
goto endjob; goto endjob;
} }
} }
if (VIR_ALLOC(mirror) < 0) if (!mirror->format) {
goto endjob; if (!(flags & VIR_DOMAIN_BLOCK_COPY_REUSE_EXT)) {
/* XXX Allow non-file mirror destinations */
mirror->type = flags & VIR_DOMAIN_BLOCK_REBASE_COPY_DEV ?
VIR_STORAGE_TYPE_BLOCK : VIR_STORAGE_TYPE_FILE;
if (format) {
if ((mirror->format = virStorageFileFormatTypeFromString(format)) <= 0) {
virReportError(VIR_ERR_INVALID_ARG, _("unrecognized format '%s'"),
format);
goto endjob;
}
} else {
if (!(flags & VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT)) {
mirror->format = disk->src->format; mirror->format = disk->src->format;
} else { } else {
/* If the user passed the REUSE_EXT flag, then either they /* If the user passed the REUSE_EXT flag, then either they
* also passed the RAW flag (and format is non-NULL), or it is * can also pass the RAW flag or use XML to tell us the format.
* safe for us to probe the format from the file that we will * So if we get here, we assume it is safe for us to probe the
* be using. */ * format from the file that we will be using. */
mirror->format = virStorageFileProbeFormat(dest, cfg->user, mirror->format = virStorageFileProbeFormat(mirror->path, cfg->user,
cfg->group); cfg->group);
} }
} }
/* pre-create the image file */ /* pre-create the image file */
if (!(flags & VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT)) { if (!(flags & VIR_DOMAIN_BLOCK_COPY_REUSE_EXT)) {
int fd = qemuOpenFile(driver, vm, dest, O_WRONLY | O_TRUNC | O_CREAT, int fd = qemuOpenFile(driver, vm, mirror->path,
O_WRONLY | O_TRUNC | O_CREAT,
&need_unlink, NULL); &need_unlink, NULL);
if (fd < 0) if (fd < 0)
goto endjob; goto endjob;
VIR_FORCE_CLOSE(fd); VIR_FORCE_CLOSE(fd);
} }
if (!format && mirror->format > 0) if (mirror->format > 0)
format = virStorageFileFormatTypeToString(mirror->format); format = virStorageFileFormatTypeToString(mirror->format);
if (VIR_STRDUP(mirror->path, dest) < 0)
goto endjob;
if (virStorageSourceInitChainElement(mirror, disk->src, false) < 0) if (virStorageSourceInitChainElement(mirror, disk->src, false) < 0)
goto endjob; goto endjob;
@ -15452,8 +15449,8 @@ qemuDomainBlockCopy(virDomainObjPtr vm,
/* Actually start the mirroring */ /* Actually start the mirroring */
qemuDomainObjEnterMonitor(driver, vm); qemuDomainObjEnterMonitor(driver, vm);
ret = qemuMonitorDriveMirror(priv->mon, device, dest, format, bandwidth, ret = qemuMonitorDriveMirror(priv->mon, device, mirror->path, format,
flags); bandwidth, flags);
virDomainAuditDisk(vm, NULL, mirror, "mirror", ret >= 0); virDomainAuditDisk(vm, NULL, mirror, "mirror", ret >= 0);
qemuDomainObjExitMonitor(driver, vm); qemuDomainObjExitMonitor(driver, vm);
if (ret < 0) { if (ret < 0) {
@ -15473,8 +15470,8 @@ qemuDomainBlockCopy(virDomainObjPtr vm,
vm->def->name); vm->def->name);
endjob: endjob:
if (need_unlink && unlink(dest)) if (need_unlink && unlink(mirror->path))
VIR_WARN("unable to unlink just-created %s", dest); VIR_WARN("unable to unlink just-created %s", mirror->path);
virStorageSourceFree(mirror); virStorageSourceFree(mirror);
if (!qemuDomainObjEndJob(driver, vm)) if (!qemuDomainObjEndJob(driver, vm))
vm = NULL; vm = NULL;
@ -15492,9 +15489,9 @@ qemuDomainBlockRebase(virDomainPtr dom, const char *path, const char *base,
unsigned long bandwidth, unsigned int flags) unsigned long bandwidth, unsigned int flags)
{ {
virDomainObjPtr vm; virDomainObjPtr vm;
const char *format = NULL;
int ret = -1; int ret = -1;
unsigned long long speed = bandwidth; unsigned long long speed = bandwidth;
virStorageSourcePtr dest = NULL;
virCheckFlags(VIR_DOMAIN_BLOCK_REBASE_SHALLOW | virCheckFlags(VIR_DOMAIN_BLOCK_REBASE_SHALLOW |
VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT | VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |
@ -15516,8 +15513,14 @@ qemuDomainBlockRebase(virDomainPtr dom, const char *path, const char *base,
BLOCK_JOB_PULL, flags); BLOCK_JOB_PULL, flags);
/* If we got here, we are doing a block copy rebase. */ /* If we got here, we are doing a block copy rebase. */
if (VIR_ALLOC(dest) < 0)
goto cleanup;
dest->type = (flags & VIR_DOMAIN_BLOCK_REBASE_COPY_DEV) ?
VIR_STORAGE_TYPE_BLOCK : VIR_STORAGE_TYPE_FILE;
if (VIR_STRDUP(dest->path, base) < 0)
goto cleanup;
if (flags & VIR_DOMAIN_BLOCK_REBASE_COPY_RAW) if (flags & VIR_DOMAIN_BLOCK_REBASE_COPY_RAW)
format = "raw"; dest->format = VIR_STORAGE_FILE_RAW;
/* Convert bandwidth MiB to bytes */ /* Convert bandwidth MiB to bytes */
if (speed > LLONG_MAX >> 20) { if (speed > LLONG_MAX >> 20) {
@ -15537,15 +15540,20 @@ qemuDomainBlockRebase(virDomainPtr dom, const char *path, const char *base,
goto cleanup; goto cleanup;
} }
/* We rely on the fact that VIR_DOMAIN_BLOCK_REBASE_SHALLOW
* and VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT map to the same values
* as for block copy. */
flags &= (VIR_DOMAIN_BLOCK_REBASE_SHALLOW | flags &= (VIR_DOMAIN_BLOCK_REBASE_SHALLOW |
VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT | VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT);
VIR_DOMAIN_BLOCK_REBASE_COPY_DEV); ret = qemuDomainBlockCopyCommon(vm, dom->conn, path, dest,
ret = qemuDomainBlockCopy(vm, dom->conn, path, base, format,
bandwidth, flags); bandwidth, flags);
vm = NULL; vm = NULL;
dest = NULL;
cleanup: cleanup:
if (vm) if (vm)
virObjectUnlock(vm); virObjectUnlock(vm);
virStorageSourceFree(dest);
return ret; return ret;
} }