qemu: Manage /dev entry on disk hotplug

When attaching a device to a domain that's using separate mount
namespace we must maintain /dev entries in order for qemu process
to see them.

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
Michal Privoznik 2016-11-15 16:53:04 +01:00
parent eadaa97548
commit 81df21507b
6 changed files with 280 additions and 9 deletions

View File

@ -53,6 +53,11 @@
#include "storage/storage_driver.h" #include "storage/storage_driver.h"
#ifdef MAJOR_IN_MKDEV
# include <sys/mkdev.h>
#elif MAJOR_IN_SYSMACROS
# include <sys/sysmacros.h>
#endif
#include <sys/time.h> #include <sys/time.h>
#include <fcntl.h> #include <fcntl.h>
#if defined(HAVE_SYS_MOUNT_H) #if defined(HAVE_SYS_MOUNT_H)
@ -7460,3 +7465,212 @@ qemuDomainDeleteNamespace(virQEMUDriverPtr driver,
virStringListFreeCount(devMountsPath, ndevMountsPath); virStringListFreeCount(devMountsPath, ndevMountsPath);
VIR_FREE(devPath); VIR_FREE(devPath);
} }
struct qemuDomainAttachDeviceMknodData {
virQEMUDriverPtr driver;
virDomainObjPtr vm;
virDomainDeviceDefPtr devDef;
const char *file;
struct stat sb;
void *acl;
};
static int
qemuDomainAttachDeviceMknodHelper(pid_t pid ATTRIBUTE_UNUSED,
void *opaque)
{
struct qemuDomainAttachDeviceMknodData *data = opaque;
int ret = -1;
virSecurityManagerPostFork(data->driver->securityManager);
if (virFileMakeParentPath(data->file) < 0) {
virReportSystemError(errno,
_("Unable to create %s"), data->file);
goto cleanup;
}
VIR_DEBUG("Creating dev %s (%d,%d)",
data->file, major(data->sb.st_rdev), minor(data->sb.st_rdev));
if (mknod(data->file, data->sb.st_mode, data->sb.st_rdev) < 0) {
/* Because we are not removing devices on hotunplug, or
* we might be creating part of backing chain that
* already exist due to a different disk plugged to
* domain, accept EEXIST. */
if (errno != EEXIST) {
virReportSystemError(errno,
_("Unable to create device %s"),
data->file);
goto cleanup;
}
}
if (virFileSetACLs(data->file, data->acl) < 0 &&
errno != ENOTSUP) {
virReportSystemError(errno,
_("Unable to set ACLs on %s"), data->file);
goto cleanup;
}
switch ((virDomainDeviceType) data->devDef->type) {
case VIR_DOMAIN_DEVICE_DISK: {
virDomainDiskDefPtr def = data->devDef->data.disk;
char *tmpsrc = def->src->path;
def->src->path = (char *) data->file;
if (virSecurityManagerSetDiskLabel(data->driver->securityManager,
data->vm->def, def) < 0) {
def->src->path = tmpsrc;
goto cleanup;
}
def->src->path = tmpsrc;
} break;
case VIR_DOMAIN_DEVICE_NONE:
case VIR_DOMAIN_DEVICE_LEASE:
case VIR_DOMAIN_DEVICE_FS:
case VIR_DOMAIN_DEVICE_NET:
case VIR_DOMAIN_DEVICE_INPUT:
case VIR_DOMAIN_DEVICE_SOUND:
case VIR_DOMAIN_DEVICE_VIDEO:
case VIR_DOMAIN_DEVICE_HOSTDEV:
case VIR_DOMAIN_DEVICE_WATCHDOG:
case VIR_DOMAIN_DEVICE_CONTROLLER:
case VIR_DOMAIN_DEVICE_GRAPHICS:
case VIR_DOMAIN_DEVICE_HUB:
case VIR_DOMAIN_DEVICE_REDIRDEV:
case VIR_DOMAIN_DEVICE_SMARTCARD:
case VIR_DOMAIN_DEVICE_CHR:
case VIR_DOMAIN_DEVICE_MEMBALLOON:
case VIR_DOMAIN_DEVICE_NVRAM:
case VIR_DOMAIN_DEVICE_RNG:
case VIR_DOMAIN_DEVICE_SHMEM:
case VIR_DOMAIN_DEVICE_TPM:
case VIR_DOMAIN_DEVICE_PANIC:
case VIR_DOMAIN_DEVICE_MEMORY:
case VIR_DOMAIN_DEVICE_IOMMU:
case VIR_DOMAIN_DEVICE_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unexpected device type %d"),
data->devDef->type);
goto cleanup;
}
ret = 0;
cleanup:
if (ret < 0)
unlink(data->file);
virFileFreeACLs(&data->acl);
return ret;
}
static int
qemuDomainAttachDeviceMknod(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainDeviceDefPtr devDef,
const char *file)
{
struct qemuDomainAttachDeviceMknodData data;
int ret = -1;
memset(&data, 0, sizeof(data));
data.driver = driver;
data.vm = vm;
data.devDef = devDef;
data.file = file;
if (stat(file, &data.sb) < 0) {
virReportSystemError(errno,
_("Unable to access %s"), file);
return ret;
}
if (virFileGetACLs(file, &data.acl) < 0 &&
errno != ENOTSUP) {
virReportSystemError(errno,
_("Unable to get ACLs on %s"), file);
return ret;
}
if (virSecurityManagerPreFork(driver->securityManager) < 0)
goto cleanup;
if (virProcessRunInMountNamespace(vm->pid,
qemuDomainAttachDeviceMknodHelper,
&data) < 0) {
virSecurityManagerPostFork(driver->securityManager);
goto cleanup;
}
virSecurityManagerPostFork(driver->securityManager);
ret = 0;
cleanup:
virFileFreeACLs(&data.acl);
return 0;
}
int
qemuDomainNamespaceSetupDisk(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainDiskDefPtr disk)
{
virDomainDeviceDef dev = {.type = VIR_DOMAIN_DEVICE_DISK, .data.disk = disk};
virStorageSourcePtr next;
const char *src = NULL;
struct stat sb;
int ret = -1;
if (!qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
return 0;
for (next = disk->src; next; next = next->backingStore) {
if (!next->path || !virStorageSourceIsBlockLocal(disk->src) ||
!STRPREFIX(next->path, "/dev")) {
/* Not creating device. Just continue. */
continue;
}
if (stat(next->path, &sb) < 0) {
virReportSystemError(errno,
_("Unable to access %s"), src);
goto cleanup;
}
if (!S_ISBLK(sb.st_mode)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Disk source %s must be a block device"),
src);
goto cleanup;
}
if (qemuDomainAttachDeviceMknod(driver,
vm,
&dev,
next->path) < 0)
goto cleanup;
}
ret = 0;
cleanup:
return ret;
}
int
qemuDomainNamespaceTeardownDisk(virQEMUDriverPtr driver ATTRIBUTE_UNUSED,
virDomainObjPtr vm ATTRIBUTE_UNUSED,
virDomainDiskDefPtr disk ATTRIBUTE_UNUSED)
{
/* While in hotplug case we create the whole backing chain,
* here we must limit ourselves. The disk we want to remove
* might be a part of backing chain of another disk.
* If you are reading these lines and have some spare time
* you can come up with and algorithm that checks for that.
* I don't, therefore: */
return 0;
}

View File

@ -805,4 +805,12 @@ int qemuDomainCreateNamespace(virQEMUDriverPtr driver,
void qemuDomainDeleteNamespace(virQEMUDriverPtr driver, void qemuDomainDeleteNamespace(virQEMUDriverPtr driver,
virDomainObjPtr vm); virDomainObjPtr vm);
int qemuDomainNamespaceSetupDisk(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainDiskDefPtr disk);
int qemuDomainNamespaceTeardownDisk(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainDiskDefPtr disk);
#endif /* __QEMU_DOMAIN_H__ */ #endif /* __QEMU_DOMAIN_H__ */

View File

@ -57,6 +57,7 @@
#include "qemu_process.h" #include "qemu_process.h"
#include "qemu_migration.h" #include "qemu_migration.h"
#include "qemu_blockjob.h" #include "qemu_blockjob.h"
#include "qemu_security.h"
#include "virerror.h" #include "virerror.h"
#include "virlog.h" #include "virlog.h"
@ -16146,9 +16147,9 @@ qemuDomainBlockPivot(virQEMUDriverPtr driver,
disk->mirror->format != VIR_STORAGE_FILE_RAW && disk->mirror->format != VIR_STORAGE_FILE_RAW &&
(virDomainLockDiskAttach(driver->lockManager, cfg->uri, vm, (virDomainLockDiskAttach(driver->lockManager, cfg->uri, vm,
disk) < 0 || disk) < 0 ||
qemuDomainNamespaceSetupDisk(driver, vm, disk) < 0 ||
qemuSetupDiskCgroup(vm, disk) < 0 || qemuSetupDiskCgroup(vm, disk) < 0 ||
virSecurityManagerSetDiskLabel(driver->securityManager, vm->def, qemuSecuritySetDiskLabel(driver, vm, disk) < 0))
disk) < 0))
goto cleanup; goto cleanup;
disk->src = oldsrc; disk->src = oldsrc;

View File

@ -34,6 +34,7 @@
#include "qemu_hostdev.h" #include "qemu_hostdev.h"
#include "qemu_interface.h" #include "qemu_interface.h"
#include "qemu_process.h" #include "qemu_process.h"
#include "qemu_security.h"
#include "domain_audit.h" #include "domain_audit.h"
#include "netdev_bandwidth_conf.h" #include "netdev_bandwidth_conf.h"
#include "domain_nwfilter.h" #include "domain_nwfilter.h"
@ -109,13 +110,15 @@ qemuDomainPrepareDisk(virQEMUDriverPtr driver,
vm, disk) < 0) vm, disk) < 0)
goto cleanup; goto cleanup;
if (virSecurityManagerSetDiskLabel(driver->securityManager, if (qemuSecuritySetDiskLabel(driver, vm, disk) < 0)
vm->def, disk) < 0)
goto rollback_lock; goto rollback_lock;
if (qemuSetupDiskCgroup(vm, disk) < 0) if (qemuDomainNamespaceSetupDisk(driver, vm, disk) < 0)
goto rollback_label; goto rollback_label;
if (qemuSetupDiskCgroup(vm, disk) < 0)
goto rollback_namespace;
ret = 0; ret = 0;
goto cleanup; goto cleanup;
@ -123,10 +126,13 @@ qemuDomainPrepareDisk(virQEMUDriverPtr driver,
if (qemuTeardownDiskCgroup(vm, disk) < 0) if (qemuTeardownDiskCgroup(vm, disk) < 0)
VIR_WARN("Unable to tear down cgroup access on %s", VIR_WARN("Unable to tear down cgroup access on %s",
virDomainDiskGetSource(disk)); virDomainDiskGetSource(disk));
rollback_namespace:
if (qemuDomainNamespaceTeardownDisk(driver, vm, disk) < 0)
VIR_WARN("Unable to remove /dev entry for %s",
virDomainDiskGetSource(disk));
rollback_label: rollback_label:
if (virSecurityManagerRestoreDiskLabel(driver->securityManager, if (qemuSecurityRestoreDiskLabel(driver, vm, disk) < 0)
vm->def, disk) < 0)
VIR_WARN("Unable to restore security label on %s", VIR_WARN("Unable to restore security label on %s",
virDomainDiskGetSource(disk)); virDomainDiskGetSource(disk));
@ -3588,8 +3594,7 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver,
qemuDomainReleaseDeviceAddress(vm, &disk->info, src); qemuDomainReleaseDeviceAddress(vm, &disk->info, src);
if (virSecurityManagerRestoreDiskLabel(driver->securityManager, if (qemuSecurityRestoreDiskLabel(driver, vm, disk) < 0)
vm->def, disk) < 0)
VIR_WARN("Unable to restore security label on %s", src); VIR_WARN("Unable to restore security label on %s", src);
if (qemuTeardownDiskCgroup(vm, disk) < 0) if (qemuTeardownDiskCgroup(vm, disk) < 0)
@ -3598,6 +3603,9 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver,
if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0)
VIR_WARN("Unable to release lock on %s", src); VIR_WARN("Unable to release lock on %s", src);
if (qemuDomainNamespaceTeardownDisk(driver, vm, disk) < 0)
VIR_WARN("Unable to remove /dev entry for %s", src);
dev.type = VIR_DOMAIN_DEVICE_DISK; dev.type = VIR_DOMAIN_DEVICE_DISK;
dev.data.disk = disk; dev.data.disk = disk;
ignore_value(qemuRemoveSharedDevice(driver, &dev, vm->def->name)); ignore_value(qemuRemoveSharedDevice(driver, &dev, vm->def->name));

View File

@ -130,3 +130,35 @@ qemuSecurityRestoreAllLabel(virQEMUDriverPtr driver,
migrated); migrated);
} }
} }
int
qemuSecuritySetDiskLabel(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainDiskDefPtr disk)
{
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT)) {
/* Already handled by namespace code. */
return 0;
}
return virSecurityManagerSetDiskLabel(driver->securityManager,
vm->def,
disk);
}
int
qemuSecurityRestoreDiskLabel(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainDiskDefPtr disk)
{
if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT)) {
/* Already handled by namespace code. */
return 0;
}
return virSecurityManagerRestoreDiskLabel(driver->securityManager,
vm->def,
disk);
}

View File

@ -36,4 +36,12 @@ int qemuSecuritySetAllLabel(virQEMUDriverPtr driver,
void qemuSecurityRestoreAllLabel(virQEMUDriverPtr driver, void qemuSecurityRestoreAllLabel(virQEMUDriverPtr driver,
virDomainObjPtr vm, virDomainObjPtr vm,
bool migrated); bool migrated);
int qemuSecuritySetDiskLabel(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainDiskDefPtr disk);
int qemuSecurityRestoreDiskLabel(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainDiskDefPtr disk);
#endif /* __QEMU_SECURITY_H__ */ #endif /* __QEMU_SECURITY_H__ */