*
* Returns: 0 on success
*/
-int virCgroupAllowDevice(virCgroupPtr group,
- char type,
- int major,
- int minor)
+int virCgroupAllowDevice(virCgroupPtr group, char type, int major, int minor)
{
int rc;
char *devstr = NULL;
*
* Returns: 0 on success
*/
-int virCgroupAllowDeviceMajor(virCgroupPtr group,
- char type,
- int major)
+int virCgroupAllowDeviceMajor(virCgroupPtr group, char type, int major)
{
int rc;
char *devstr = NULL;
return rc;
}
+/**
+ * virCgroupAllowDevicePath:
+ *
+ * @group: The cgroup to allow the device for
+ * @path: the device to allow
+ *
+ * Queries the type of device and its major/minor number, and
+ * adds that to the cgroup ACL
+ *
+ * Returns: 0 on success
+ */
+int virCgroupAllowDevicePath(virCgroupPtr group, const char *path)
+{
+ struct stat sb;
+
+ if (stat(path, &sb) < 0)
+ return -errno;
+
+ if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode))
+ return -EINVAL;
+
+ return virCgroupAllowDevice(group,
+ S_ISCHR(sb.st_mode) ? 'c' : 'b',
+ major(sb.st_rdev),
+ minor(sb.st_rdev));
+}
+
+/**
+ * virCgroupDenyDevice:
+ *
+ * @group: The cgroup to deny a device for
+ * @type: The device type (i.e., 'c' or 'b')
+ * @major: The major number of the device
+ * @minor: The minor number of the device
+ *
+ * Returns: 0 on success
+ */
+int virCgroupDenyDevice(virCgroupPtr group, char type, int major, int minor)
+{
+ int rc;
+ char *devstr = NULL;
+
+ if (virAsprintf(&devstr, "%c %i:%i rwm", type, major, minor) == -1) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = virCgroupSetValueStr(group,
+ VIR_CGROUP_CONTROLLER_DEVICES,
+ "devices.deny",
+ devstr);
+out:
+ VIR_FREE(devstr);
+
+ return rc;
+}
+
+/**
+ * virCgroupDenyDeviceMajor:
+ *
+ * @group: The cgroup to deny an entire device major type for
+ * @type: The device type (i.e., 'c' or 'b')
+ * @major: The major number of the device type
+ *
+ * Returns: 0 on success
+ */
+int virCgroupDenyDeviceMajor(virCgroupPtr group, char type, int major)
+{
+ int rc;
+ char *devstr = NULL;
+
+ if (virAsprintf(&devstr, "%c %i:* rwm", type, major) == -1) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = virCgroupSetValueStr(group,
+ VIR_CGROUP_CONTROLLER_DEVICES,
+ "devices.deny",
+ devstr);
+ out:
+ VIR_FREE(devstr);
+
+ return rc;
+}
+
+int virCgroupDenyDevicePath(virCgroupPtr group, const char *path)
+{
+ struct stat sb;
+
+ if (stat(path, &sb) < 0)
+ return -errno;
+
+ if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode))
+ return -EINVAL;
+
+ return virCgroupDenyDevice(group,
+ S_ISCHR(sb.st_mode) ? 'c' : 'b',
+ major(sb.st_rdev),
+ minor(sb.st_rdev));
+}
+
int virCgroupSetCpuShares(virCgroupPtr group, unsigned long long shares)
{
return virCgroupSetValueU64(group,
return -1;
}
+static const char *const defaultDeviceACL[] = {
+ "/dev/null", "/dev/full", "/dev/zero",
+ "/dev/random", "/dev/urandom",
+ "/dev/ptmx", "/dev/kvm", "/dev/kqemu",
+ "/dev/rtc", "/dev/hpet", "/dev/net/tun",
+ NULL,
+};
+#define DEVICE_PTY_MAJOR 136
+#define DEVICE_SND_MAJOR 116
+
static int qemuSetupCgroup(virConnectPtr conn,
struct qemud_driver *driver,
virDomainObjPtr vm)
{
virCgroupPtr cgroup = NULL;
int rc;
+ unsigned int i;
+ const char *const *deviceACL = defaultDeviceACL;
if (driver->cgroup == NULL)
return 0; /* Not supported, so claim success */
goto cleanup;
}
+ rc = virCgroupDenyAllDevices(cgroup);
+ if (rc != 0) {
+ if (rc == -EPERM) {
+ VIR_WARN0("Group devices ACL is not accessible, disabling whitelisting");
+ goto done;
+ }
+
+ virReportSystemError(conn, -rc,
+ _("Unable to deny all devices for %s"), vm->def->name);
+ goto cleanup;
+ }
+
+ for (i = 0; i < vm->def->ndisks ; i++) {
+ if (vm->def->disks[i]->type != VIR_DOMAIN_DISK_TYPE_BLOCK ||
+ vm->def->disks[i]->src == NULL)
+ continue;
+
+ rc = virCgroupAllowDevicePath(cgroup,
+ vm->def->disks[i]->src);
+ if (rc != 0) {
+ virReportSystemError(conn, -rc,
+ _("Unable to allow device %s for %s"),
+ vm->def->disks[i]->src, vm->def->name);
+ goto cleanup;
+ }
+ }
+
+ rc = virCgroupAllowDeviceMajor(cgroup, 'c', DEVICE_PTY_MAJOR);
+ if (rc != 0) {
+ virReportSystemError(conn, -rc, "%s",
+ _("unable to allow /dev/pts/ devices"));
+ goto cleanup;
+ }
+
+ if (vm->def->nsounds) {
+ rc = virCgroupAllowDeviceMajor(cgroup, 'c', DEVICE_SND_MAJOR);
+ if (rc != 0) {
+ virReportSystemError(conn, -rc, "%s",
+ _("unable to allow /dev/snd/ devices"));
+ goto cleanup;
+ }
+ }
+
+ for (i = 0; deviceACL[i] != NULL ; i++) {
+ rc = virCgroupAllowDevicePath(cgroup,
+ deviceACL[i]);
+ if (rc < 0 &&
+ rc != -ENOENT) {
+ virReportSystemError(conn, -rc,
+ _("unable to allow device %s"),
+ deviceACL[i]);
+ goto cleanup;
+ }
+ }
+
+done:
virCgroupFree(&cgroup);
return 0;
virDomainObjPtr vm;
virDomainDeviceDefPtr dev = NULL;
unsigned int qemuCmdFlags;
+ virCgroupPtr cgroup = NULL;
int ret = -1;
qemuDriverLock(driver);
goto cleanup;
if (dev->type == VIR_DOMAIN_DEVICE_DISK) {
+ if (driver->cgroup != NULL) {
+ if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) !=0 ) {
+ qemudReportError(dom->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Unable to find cgroup for %s\n"),
+ vm->def->name);
+ goto cleanup;
+ }
+ if (dev->data.disk->src != NULL &&
+ dev->data.disk->type == VIR_DOMAIN_DISK_TYPE_BLOCK &&
+ virCgroupAllowDevicePath(cgroup,
+ dev->data.disk->src) < 0) {
+ qemudReportError(dom->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("unable to allow device %s"),
+ dev->data.disk->src);
+ goto cleanup;
+ }
+ }
+
+ if (driver->securityDriver)
+ driver->securityDriver->domainSetSecurityImageLabel(dom->conn, vm, dev->data.disk);
+
switch (dev->data.disk->device) {
case VIR_DOMAIN_DISK_DEVICE_CDROM:
case VIR_DOMAIN_DISK_DEVICE_FLOPPY:
qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
_("disk bus '%s' cannot be hotplugged."),
virDomainDiskBusTypeToString(dev->data.disk->bus));
- goto cleanup;
+ /* fallthrough */
}
break;
qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
_("disk device type '%s' cannot be hotplugged"),
virDomainDiskDeviceTypeToString(dev->data.disk->device));
- goto cleanup;
+ /* Fallthrough */
+ }
+ if (ret != 0) {
+ virCgroupDenyDevicePath(cgroup,
+ dev->data.disk->src);
}
} else if (dev->type == VIR_DOMAIN_DEVICE_NET) {
ret = qemudDomainAttachNetDevice(dom->conn, vm, dev, qemuCmdFlags);
ret = -1;
cleanup:
+ if (cgroup)
+ virCgroupFree(&cgroup);
+
if (ret < 0) {
if (qemuDomainSetDeviceOwnership(dom->conn, driver, dev, 1) < 0)
VIR_WARN0("Fail to restore disk device ownership");