]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
sanlock: Add option to check ownership of disk leases
authorJim Fehlig <jfehlig@suse.com>
Wed, 11 Mar 2026 23:16:53 +0000 (17:16 -0600)
committerJim Fehlig <jfehlig@suse.com>
Fri, 17 Jul 2026 16:57:16 +0000 (10:57 -0600)
In a clustered environment it may be desirable to provision disk leases
for use by a specific domain. This patch adds a 'check_disk_lease_owner'
option to the sanlock driver to check that leases are owned by the domain
attempting to acquire them. sanlock's Lock Value Block (LVB) is used to
store the owning domain UUID within the lease. When a domain is started
and attempts to acquire its leases, the sanlock driver will ensure its
UUID matches the UUID recorded in the leases. Any mismatches will cause
lease acquisition and domain startup to fail.

The 'check_disk_lease_owner' option is disabled by default. When enabled,
it can be used with auto_disk_leases or leases managed by an external
application.

Signed-off-by: Jim Fehlig <jfehlig@suse.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
src/locking/libvirt_sanlock.aug
src/locking/lock_driver_sanlock.c
src/locking/sanlock.conf
src/locking/test_libvirt_sanlock.aug.in

index 62d0672952e9647c5082220063bdcebf112c48bf..af6c25755fb395235b46adf87b6234e114b84510 100644 (file)
@@ -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 ]
 
index a07f3652c1e7606e01759d26116347bdeb00afa1..40aae630d1e02febb9bac465edb8b5eba41852c8 100644 (file)
@@ -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);
index 3c356bef9c51a95020de79d1ea410b91096b2dc8..6953e5889b787e56041c337fb2c6a0d16bd6915f 100644 (file)
 # 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
index 5eabb6726de4f3bf62fa8e462a426b118f59c4ff..e468a2cdcd06d4c91c72d1edfdf1ece7d36731b2 100644 (file)
@@ -9,3 +9,4 @@ module Test_libvirt_sanlock =
 { "io_timeout" = "0" }
 { "user" = "root" }
 { "group" = "root" }
+{ "check_disk_lease_owner" = "0" }