On 10/17/2012 06:30 PM, Eric Blake wrote:
I finally have all the pieces in place to perform a block-commit
with
SELinux enforcing. There's still missing cleanup work when the commit
completes, but doing that requires tracking both the backing chain and
the base and top files within that chain in domain XML across libvirtd
restarts. Furthermore, from a security standpoint, once you have
granted access, you must assume any damage that can be done will be
done; later revoking access is nice to minimize the window of damage,
but less important as it does not affect the fact that damage can be
done in the first place. Therefore, saving the revoke efforts until
we have better XML tracking of what chain operations are in effect,
including across a libvirtd restart, is reasonable.
Umm. I don't know that I agree with your statement about the relative
importance of undoing all the labelling when you're done, but if the
alternative to this is to require turning off selinux, then it's
definitely a win.
* src/qemu/qemu_driver.c (qemuDomainBlockCommit): Label disks as
needed.
(qemuDomainPrepareDiskChainElement): Cast away const.
---
v3: enhance commit message
src/qemu/qemu_driver.c | 42 +++++++++++++++++++++++++++++++++++++-----
1 file changed, 37 insertions(+), 5 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 072ec17..3829a89 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -10469,7 +10469,7 @@ qemuDomainPrepareDiskChainElement(struct qemud_driver *driver,
virDomainObjPtr vm,
virCgroupPtr cgroup,
virDomainDiskDefPtr disk,
- char *file,
+ const char *file,
qemuDomainDiskChainMode mode)
{
/* The easiest way to label a single file with the same
@@ -10481,7 +10481,7 @@ qemuDomainPrepareDiskChainElement(struct qemud_driver *driver,
bool origreadonly = disk->readonly;
int ret = -1;
- disk->src = file;
+ disk->src = (char *) file; /* casting away const is safe here */
... since you're misusing the diskdef anyway :-)
disk->format = VIR_STORAGE_FILE_RAW;
disk->backingChain = NULL;
disk->readonly = mode == VIR_DISK_CHAIN_READ_ONLY;
@@ -12699,6 +12699,7 @@ qemuDomainBlockCommit(virDomainPtr dom, const char *path, const
char *base,
virStorageFileMetadataPtr top_meta = NULL;
const char *top_parent = NULL;
const char *base_canon = NULL;
+ virCgroupPtr cgroup = NULL;
virCheckFlags(VIR_DOMAIN_BLOCK_COMMIT_SHALLOW, -1);
@@ -12774,15 +12775,46 @@ qemuDomainBlockCommit(virDomainPtr dom, const char *path, const
char *base,
goto endjob;
}
- /* XXX We need to be changing the SELinux label on both 'base' and
- * the parent of 'top', so that qemu can open(O_RDWR) those files
- * for the duration of the commit. */
+ /* For the commit to succeed, we must allow qemu to open both the
+ * 'base' image and the parent of 'top' as read/write; 'top'
might
+ * not have a parent, or might already be read-write. XXX It
+ * would also be nice to revert 'base' to read-only, as well as
+ * revoke access to files removed from the chain, when the commit
+ * operation succeeds, but doing that requires tracking the
+ * operation in XML across libvirtd restarts. */
+ if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES) &&
+ virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0)) {
It looks to me like everything in cgroup.c returns < 0 ( -errno in most,
if not all cases). Any idea why all its callers are testing for != 0
instead of the more standard < 0 ?
(you're just following the convention, so no complaints on your patch,
but it looks a bit odd.)
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to find cgroup for %s"),
+ vm->def->name);
+ goto endjob;
+ }
+ if (qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, base_canon,
+ VIR_DISK_CHAIN_READ_WRITE) < 0 ||
+ (top_parent && top_parent != disk->src &&
+ qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk,
+ top_parent,
+ VIR_DISK_CHAIN_READ_WRITE) < 0))
+ goto endjob;
+
+ /* Start the commit operation. */
qemuDomainObjEnterMonitor(driver, vm);
ret = qemuMonitorBlockCommit(priv->mon, device, top_canon, base_canon,
bandwidth);
qemuDomainObjExitMonitor(driver, vm);
endjob:
+ if (ret < 0) {
+ /* Revert access to read-only, if possible. */
+ qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, base_canon,
+ VIR_DISK_CHAIN_READ_ONLY);
+ if (top_parent && top_parent != disk->src)
+ qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk,
+ top_parent,
+ VIR_DISK_CHAIN_READ_ONLY);
Is it always the case that it was previously read-only, or is this just
a naive assumption?
+ }
+ if (cgroup)
+ virCgroupFree(&cgroup);
if (qemuDomainObjEndJob(driver, vm) == 0) {
vm = NULL;
goto cleanup;
ACK.