diff --git a/src/locking/libvirt_sanlock.aug b/src/locking/libvirt_sanlock.aug index 62d0672952..af6c25755f 100644 --- a/src/locking/libvirt_sanlock.aug +++ b/src/locking/libvirt_sanlock.aug @@ -25,6 +25,7 @@ module Libvirt_sanlock = | int_entry "io_timeout" | str_entry "user" | str_entry "group" + | bool_entry "check_disk_lease_owner" let comment = [ label "#comment" . del /#[ \t]*/ "# " . store /([^ \t\n][^\n]*)?/ . del /\n/ "\n" ] let empty = [ label "#empty" . eol ] diff --git a/src/locking/lock_driver_sanlock.c b/src/locking/lock_driver_sanlock.c index a07f3652c1..40aae630d1 100644 --- a/src/locking/lock_driver_sanlock.c +++ b/src/locking/lock_driver_sanlock.c @@ -67,6 +67,7 @@ struct _virLockManagerSanlockDriver { unsigned int hostID; bool autoDiskLease; char *autoDiskLeasePath; + bool checkDiskLeaseOwner; unsigned int io_timeout; /* under which permissions does sanlock run */ @@ -147,6 +148,10 @@ virLockManagerSanlockLoadConfig(virLockManagerSanlockDriver *driver, if (virConfGetValueBool(conf, "require_lease_for_disks", &driver->requireLeaseForDisks) < 0) return -1; + if (virConfGetValueBool(conf, "check_disk_lease_owner", + &driver->checkDiskLeaseOwner) < 0) + return -1; + if (virConfGetValueUInt(conf, "io_timeout", &driver->io_timeout) < 0) return -1; @@ -839,6 +844,72 @@ virLockManagerSanlockRegisterKillscript(int sock, return 0; } +static int virLockManagerSanlockCheckOwner(virLockManagerSanlockDriver *driver, + virLockManagerSanlockPrivate *priv) +{ + char lvb[VIR_UUID_STRING_BUFLEN] = {0}; + char vm_uuidstr[VIR_UUID_STRING_BUFLEN]; + int rv; + size_t i; + + if (!driver->checkDiskLeaseOwner) + return 0; + + virUUIDFormat(priv->vm_uuid, vm_uuidstr); + + for (i = 0; i < priv->res_count; i++) { + memset(lvb, 0, sizeof(lvb)); + + rv = sanlock_get_lvb(0, priv->res_args[i], lvb, sizeof(lvb) - 1); + if (rv < 0) { + /* Failed to read LVB, treat as "legacy" lease + * without ownership tracking and skip the check. + */ + VIR_DEBUG("Failed to read LVB, skipping ownership check"); + continue; + } + + if (lvb[0] == '\0') { + VIR_DEBUG("Writing VM UUID to empty LVB"); + /* Empty LVB: this lease does not yet have an owner recorded. Write + * the domain's UUID so that subsequent acquire attempts by other + * domains will be rejected. This handles the auto_disk_leases case + * where lease files are created by libvirt. rather + */ + if ((rv = sanlock_set_lvb(0, priv->res_args[i], vm_uuidstr, + sizeof(vm_uuidstr))) < 0) { + g_autofree char *err = NULL; + if (virLockManagerSanlockError(rv, &err)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to set LVB for lease %1$s: %2$s"), + priv->res_args[i]->disks[0].path, NULLSTR(err)); + } else { + virReportSystemError(-rv, + _("Failed to set LVB for lease %1$s"), + priv->res_args[i]->disks[0].path); + } + return -1; + } + continue; + } + + VIR_DEBUG("Comparing LVB UUID '%s' to VM UUID '%s'", lvb, vm_uuidstr); + if (STRNEQ(lvb, vm_uuidstr)) { + /* + * LVB contains a different domain UUID. This lease + * was provisioned for another domain and must not + * be acquired by this one. + */ + virReportError(VIR_ERR_RESOURCE_BUSY, + _("Disk lease '%1$s' is not owned by domain with UUID '%2$s'"), + priv->res_args[i]->disks[0].path, vm_uuidstr); + return -1; + } + } + return 0; +} + + static int virLockManagerSanlockAcquire(virLockManager *lock, const char *state, unsigned int flags, @@ -934,7 +1005,8 @@ static int virLockManagerSanlockAcquire(virLockManager *lock, if (!(flags & VIR_LOCK_MANAGER_ACQUIRE_REGISTER_ONLY)) { VIR_DEBUG("Acquiring object %u", priv->res_count); - if ((rv = sanlock_acquire(sock, priv->vm_pid, 0, + if ((rv = sanlock_acquire(sock, priv->vm_pid, + driver->checkDiskLeaseOwner ? SANLK_ACQUIRE_LVB : 0, priv->res_count, priv->res_args, opt)) < 0) { char *err = NULL; @@ -949,6 +1021,9 @@ static int virLockManagerSanlockAcquire(virLockManager *lock, } goto error; } + + if (virLockManagerSanlockCheckOwner(driver, priv) < 0) + goto error; } VIR_FREE(opt); diff --git a/src/locking/sanlock.conf b/src/locking/sanlock.conf index 3c356bef9c..6953e5889b 100644 --- a/src/locking/sanlock.conf +++ b/src/locking/sanlock.conf @@ -67,3 +67,31 @@ # access them. Accepted values are described in qemu.conf. #user = "root" #group = "root" + +# +# Flag to enable verifying if disk leases have been provisioned +# for domains attempting to acquire them. This is accomplished +# using sanlock's Lock Value Block (LVB), a 512 byte application +# specific block in the lease lockspace where the domain's UUID +# can be stored when the lease is created. When libvirt starts a +# domain, the sanlock driver will check for a valid and matching +# UUID in the LVB of all leases. This prevents a lease that was +# provisioned for one domain from accidentally being used by a +# different domain. +# +# When enabled, if a lease file has an empty LVB, the UUID of the +# acquiring domain is written into the LVB automatically, so that +# subsequent lease acquisition attempts by other domains will be +# rejected. This behavior accommodates the auto_disk_leases case, +# where leases are created by libvirt. If leases are managed by +# an external application, it must write the domain UUID into the +# LVB during lease creation. +# +# libvirt will never delete the contents of the LVB, even if it +# previously wrote the UUID to an empty LVB with auto_disk_leases +# enabled. An external application must clear or update the LVB +# if the associated lease is to be used with a different domain. +# +# Defaults to disabled. +# +#check_disk_lease_owner = 0 diff --git a/src/locking/test_libvirt_sanlock.aug.in b/src/locking/test_libvirt_sanlock.aug.in index 5eabb6726d..e468a2cdcd 100644 --- a/src/locking/test_libvirt_sanlock.aug.in +++ b/src/locking/test_libvirt_sanlock.aug.in @@ -9,3 +9,4 @@ module Test_libvirt_sanlock = { "io_timeout" = "0" } { "user" = "root" } { "group" = "root" } +{ "check_disk_lease_owner" = "0" }