[libvirt] [PATCH 09/14] Add a simple internal API for managing lockspaces

Daniel P. Berrange berrange at redhat.com
Thu Jul 7 14:17:27 UTC 2011


From: "Daniel P. Berrange" <berrange at redhat.com>

This adds a simple module for managing lockspaces. A lockspace
has a path to a directory, which will usually be kep on shared
storage like NFS/GFS, etc

Inside the lockspace leases are created as plain files. Locks
on leases are taken using the virFileLock APIs (fcntl based).

Leases can be created ahead of time, or auto-created/deleted
at time of lock acquisition/release. Before creating/deleting
a lock, a lock must be held on the "index" file for the lockspace

* src/locking/lock_database.c, src/locking/lock_database.h: Simple
  internal API for lockspaces
* include/libvirt/virterror.h, src/util/virterror.c: Add
  VIR_ERR_RESOURCE_BUSY
---
 include/libvirt/virterror.h |    1 +
 src/locking/lock_database.c |  915 +++++++++++++++++++++++++++++++++++++++++++
 src/locking/lock_database.h |   43 ++
 src/util/virterror.c        |    6 +
 4 files changed, 965 insertions(+), 0 deletions(-)
 create mode 100644 src/locking/lock_database.c
 create mode 100644 src/locking/lock_database.h

diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index efa4796..84226ac 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -232,6 +232,7 @@ typedef enum {
     VIR_ERR_INVALID_DOMAIN_SNAPSHOT = 71,/* invalid domain snapshot */
     VIR_ERR_NO_DOMAIN_SNAPSHOT = 72,	/* domain snapshot not found */
     VIR_ERR_INVALID_STREAM = 73,        /* stream pointer not valid */
+    VIR_ERR_RESOURCE_BUSY = 74,         /* resource is already in use */
 } virErrorNumber;
 
 /**
diff --git a/src/locking/lock_database.c b/src/locking/lock_database.c
new file mode 100644
index 0000000..4e51548
--- /dev/null
+++ b/src/locking/lock_database.c
@@ -0,0 +1,915 @@
+
+#include <config.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "uuid.h"
+#include "lock_database.h"
+#include "virterror_internal.h"
+#include "logging.h"
+#include "memory.h"
+#include "files.h"
+#include "hash.h"
+#include "util.h"
+
+#define VIR_FROM_THIS VIR_FROM_LOCKING
+
+#define virLockError(code, ...)                                   \
+    virReportErrorHelper(VIR_FROM_THIS, code, __FILE__,           \
+                         __FUNCTION__, __LINE__, __VA_ARGS__)
+
+/*
+ * Approach to handling locking:
+ *
+ *  - Lock storage area, identified by a path to a directory
+ *
+ *      eg /var/lib/libvirt/lock
+ *
+ *    The storage area should generally be on shared storage (NFS,
+ *    GFS, etc), unless protection is only desired within the
+ *    scope of the local machine
+ *
+ *  - Lockspaces within a storage area, identified by an
+ *    arbitrary name, which must not contain '/'. Recommended
+ *    to use domain name style lockspace names, eg org.libvirt.lockd.files
+ *
+ *      /var/lib/libvirt/lockd/org.libvirt.lockd.files
+ *
+ *  - Lockspaces contain a single 'index' file which contains
+ *    the original unhashed lockspace name.
+ *
+ *  - Lease key within a lockspace. Identified by a arbitrary
+ *    string, which must not contain '/', or be the word
+ *    'org.livirt.lockd.index'.
+ *
+ *      /var/lib/libvirt/lockd/org.libvirt.lockd.files/wibble
+ *
+ *  - When creating or deleting a lease must be held on the
+ *    'org.libvirt.lockd.index' lease of the lockspace
+ *
+ */
+
+typedef struct _virLockDatabaseLease virLockDatabaseLease;
+typedef virLockDatabaseLease *virLockDatabaseLeasePtr;
+
+typedef struct _virLockDatabaseLockspace virLockDatabaseLockspace;
+typedef virLockDatabaseLockspace *virLockDatabaseLockspacePtr;
+
+typedef struct _virLockDatabaseFile virLockDatabaseFile;
+typedef virLockDatabaseFile *virLockDatabaseFilePtr;
+
+struct _virLockDatabaseFile {
+    bool shared;
+    char *path;
+    unsigned int refs; /* # of attached users for shared locks */
+    int fd;
+};
+
+struct _virLockDatabaseLease {
+    char *key;
+
+    virLockDatabaseFile file;
+};
+
+struct _virLockDatabaseLockspace {
+    char *path;
+    char *name;
+
+    bool autoLease;
+
+    virLockDatabaseLeasePtr idx;
+
+    size_t nleases;
+    virLockDatabaseLeasePtr *leases;
+};
+
+struct _virLockDatabase {
+    unsigned char hostuuid[VIR_UUID_BUFLEN];
+    char *hostname;
+
+    size_t nlockspaces;
+    virLockDatabaseLockspacePtr *lockspaces;
+
+    virHashTablePtr files;
+};
+
+
+static void virLockDatabaseFileClear(virLockDatabaseFilePtr file)
+{
+    if (!file)
+        return;
+
+    VIR_FORCE_CLOSE(file->fd);
+    VIR_FREE(file->path);
+}
+
+
+static void virLockDatabaseLeaseFree(virLockDatabaseLeasePtr lease)
+{
+    if (!lease)
+        return;
+
+    virLockDatabaseFileClear(&lease->file);
+
+    VIR_FREE(lease->key);
+    VIR_FREE(lease);
+}
+
+
+static void virLockDatabaseLockspaceFree(virLockDatabaseLockspacePtr lkspc)
+{
+    size_t i;
+
+    if (!lkspc)
+        return;
+
+    for (i = 0 ; i < lkspc->nleases ; i++)
+        virLockDatabaseLeaseFree(lkspc->leases[i]);
+    VIR_FREE(lkspc->leases);
+
+    virLockDatabaseLeaseFree(lkspc->idx);
+
+    VIR_FREE(lkspc->path);
+    VIR_FREE(lkspc->name);
+    VIR_FREE(lkspc);
+}
+
+
+void virLockDatabaseFree(virLockDatabasePtr db)
+{
+    size_t i;
+
+    if (!db)
+        return;
+
+    virHashFree(db->files);
+
+    for (i = 0 ; i < db->nlockspaces ; i++)
+        virLockDatabaseLockspaceFree(db->lockspaces[i]);
+    VIR_FREE(db->lockspaces);
+
+    VIR_FREE(db->hostname);
+    VIR_FREE(db);
+}
+
+
+static char *virLockDatabaseBuildLeasePath(const char *path,
+                                           const char *lockspace,
+                                           const char *key)
+{
+    char *res;
+
+    if (virAsprintf(&res, "%s/%s/%s", path, lockspace, key) < 0)
+        goto no_memory;
+
+    return res;
+
+no_memory:
+    virReportOOMError();
+    return NULL;
+}
+
+
+static char *virLockDatabaseBuildLockspacePath(const char *path,
+                                               const char *lockspace)
+{
+    char *res;
+
+    if (virAsprintf(&res, "%s/%s", path, lockspace) < 0)
+        goto no_memory;
+
+    return res;
+
+no_memory:
+    virReportOOMError();
+    return NULL;
+}
+
+
+static virLockDatabaseLeasePtr virLockDatabaseLeaseNew(const char *path,
+                                                       const char *key)
+{
+    virLockDatabaseLeasePtr lease;
+
+    if (VIR_ALLOC(lease) < 0)
+        goto no_memory;
+
+    if (!(lease->key = strdup(key)))
+        goto no_memory;
+
+    if (!(lease->file.path = strdup(path)))
+        goto no_memory;
+    lease->file.fd = -1;
+
+    return lease;
+
+no_memory:
+    virReportOOMError();
+    virLockDatabaseLeaseFree(lease);
+    return NULL;
+}
+
+
+static virLockDatabaseLockspacePtr virLockDatabaseLockspaceNew(const char *path,
+                                                               const char *name,
+                                                               bool autoLease)
+{
+    virLockDatabaseLockspacePtr lkspc = NULL;
+    char *idxpath = virLockDatabaseBuildLeasePath(path, name, "org.libvirt.lockd.index");
+
+    if (!idxpath)
+        goto error;
+
+    if (VIR_ALLOC(lkspc) < 0)
+        goto no_memory;
+
+    if (!(lkspc->name = strdup(name)))
+        goto no_memory;
+
+    if (!(lkspc->path = strdup(path)))
+        goto no_memory;
+
+    lkspc->autoLease = autoLease;
+
+    if (!(lkspc->idx = virLockDatabaseLeaseNew(idxpath, "org.libvirt.lockd.index")))
+        goto error;
+
+    VIR_FREE(idxpath);
+
+    return lkspc;
+
+no_memory:
+    virReportOOMError();
+error:
+    VIR_FREE(idxpath);
+    virLockDatabaseLockspaceFree(lkspc);
+    return NULL;
+}
+
+
+virLockDatabasePtr virLockDatabaseNew(const unsigned char *hostuuid,
+                                      const char *hostname)
+{
+    virLockDatabasePtr db;
+
+    if (VIR_ALLOC(db) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    memcpy(db->hostuuid, hostuuid, VIR_UUID_BUFLEN);
+    if (!(db->hostname = strdup(hostname)))
+        goto no_memory;
+
+    if (!(db->files = virHashCreate(10, NULL)))
+        goto error;
+
+    return db;
+
+no_memory:
+    virReportOOMError();
+error:
+    virLockDatabaseFree(db);
+    return NULL;
+}
+
+
+static int virLockDatabaseLockspaceInitialize(virLockDatabaseLockspacePtr lkspc)
+{
+    int fd = -1;
+    char *lkspcpath = virLockDatabaseBuildLockspacePath(lkspc->path, lkspc->name);
+
+    if (!lkspcpath)
+        return -1;
+
+    VIR_DEBUG("Lockspace path %s", lkspcpath);
+
+    if (mkdir(lkspcpath, 0700) < 0) {
+        if (errno != EEXIST) {
+            virReportSystemError(errno,
+                                 _("Unable to create lockspace index %s"),
+                                 lkspc->idx->file.path);
+            return -1;
+        }
+    }
+
+    if ((fd = open(lkspc->idx->file.path, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0) {
+        if (errno != EEXIST) {
+            virReportSystemError(errno,
+                                 _("Unable to create lockspace index %s"),
+                                 lkspc->idx->file.path);
+            goto error;
+        }
+    }
+
+    if (VIR_CLOSE(fd) < 0) {
+        virReportSystemError(errno,
+                             _("Unable to save lockspace index %s"),
+                             lkspc->idx->file.path);
+        goto error;
+    }
+
+    return 0;
+
+error:
+    VIR_FORCE_CLOSE(fd);
+    unlink(lkspc->idx->file.path);
+    rmdir(lkspcpath);
+    return -1;
+}
+
+static virLockDatabaseLockspacePtr
+virLockDatabaseFindLockspace(virLockDatabasePtr db,
+                             const char *name)
+{
+    virLockDatabaseLockspacePtr lkspc;
+    size_t i;
+
+    for (i = 0 ; i < db->nlockspaces ; i++) {
+        lkspc = db->lockspaces[i];
+        if (STREQ(lkspc->name, name))
+            return lkspc;
+    }
+
+    return NULL;
+}
+
+static virLockDatabaseLeasePtr
+virLockDatabaseLockspaceFindLease(virLockDatabaseLockspacePtr lkspc,
+                                  const char *key)
+{
+    virLockDatabaseLeasePtr lease;
+    size_t i;
+
+    for (i = 0 ; i < lkspc->nleases ; i++) {
+        lease = lkspc->leases[i];
+        if (STREQ(lease->key, key))
+            return lease;
+    }
+
+    return NULL;
+}
+
+
+static int
+virLockDatabaseRemoveLease(virLockDatabaseLockspacePtr lkspc,
+                           virLockDatabaseLeasePtr lease)
+{
+    size_t i;
+
+    for (i = 0 ; i < lkspc->nleases ; i++) {
+        if (lkspc->leases[i] == lease)
+            break;
+    }
+
+    if (i == lkspc->nleases)
+        return -1;
+
+    if (lkspc->nleases > 1) {
+        memmove(lkspc->leases + i,
+                lkspc->leases + i + 1,
+                sizeof(*lkspc->leases) *
+                (lkspc->nleases - (i + 1)));
+        VIR_SHRINK_N(lkspc->leases, lkspc->nleases, 1);
+    } else {
+        VIR_FREE(lkspc->leases);
+        lkspc->nleases = 0;
+    }
+    return 0;
+}
+
+
+static void
+virLockDatabaseRemoveLockspace(virLockDatabasePtr db,
+                               virLockDatabaseLockspacePtr lkspc)
+{
+    size_t i;
+    bool found = false;
+
+    for (i = 0 ; i < db->nlockspaces ; i++) {
+        if (db->lockspaces[i] == lkspc) {
+            found = true;
+            break;
+        }
+    }
+
+    if (!found)
+        return;
+
+    if (db->nlockspaces > 1) {
+        memmove(db->lockspaces + i,
+                db->lockspaces + i + 1,
+                sizeof(*db->lockspaces) *
+                (db->nlockspaces - (i + 1)));
+        VIR_SHRINK_N(db->lockspaces, db->nlockspaces, 1);
+    } else {
+        VIR_FREE(db->lockspaces);
+        db->nlockspaces = 0;
+    }
+}
+
+
+int virLockDatabaseCreateLockspace(virLockDatabasePtr db,
+                                   const char *path,
+                                   const char *name,
+                                   bool autoLease)
+{
+    int ret = -1;
+    virLockDatabaseLockspacePtr lkspc;
+
+    VIR_DEBUG("db=%p path=%s name=%s autoLease=%d",
+              db, path, name, autoLease);
+
+    if ((lkspc = virLockDatabaseFindLockspace(db, name)) != NULL) {
+        if (STRNEQ(path, lkspc->path)) {
+            virLockError(VIR_ERR_INTERNAL_ERROR,
+                         _("Incorrect path %s for lockspace %s at %s"),
+                         path, name, lkspc->path);
+            return -1;
+        }
+
+        return 0;
+    }
+
+    if ((lkspc = virLockDatabaseLockspaceNew(path, name, autoLease)) == NULL)
+        return -1;
+
+    if (VIR_EXPAND_N(db->lockspaces, db->nlockspaces, 1) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    if (virLockDatabaseLockspaceInitialize(lkspc) < 0) {
+        VIR_SHRINK_N(db->lockspaces, db->nlockspaces, 1);
+        goto cleanup;
+    }
+
+    db->lockspaces[db->nlockspaces-1] = lkspc;
+
+    ret = 0;
+
+cleanup:
+    if (ret != 0)
+        virLockDatabaseLockspaceFree(lkspc);
+    return ret;
+}
+
+
+int virLockDatabaseDeleteLockspace(virLockDatabasePtr db,
+                                   const char *path,
+                                   const char *name)
+{
+    char *lkspcpath = NULL;
+    virLockDatabaseLockspacePtr lkspc;
+
+    VIR_DEBUG("db=%p path=%s name=%s",
+              db, path, name);
+
+    if (!(lkspc = virLockDatabaseFindLockspace(db, name))) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("Lockspace %s at %s does not exist"),
+                     name, path);
+        return -1;
+    }
+
+    if (STRNEQ(path, lkspc->path)) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("Incorrect path %s for lockspace %s at %s"),
+                     path, name, lkspc->path);
+        return -1;
+    }
+
+    if (!(lkspcpath = virLockDatabaseBuildLockspacePath(lkspc->path, lkspc->name)))
+        return -1;
+
+    if (unlink(lkspc->idx->file.path) < 0) {
+        if (errno != ENOENT) {
+            virReportSystemError(errno,
+                                 _("Unable to delete lockspace index %s"),
+                                 lkspc->idx->file.path);
+            return -1;
+        }
+    }
+
+    VIR_DEBUG("Lockspace path %s", lkspcpath);
+
+    if (rmdir(lkspcpath) < 0) {
+        if (errno != ENOENT) {
+            virReportSystemError(errno,
+                                 _("Unable to delete lockspace %s"),
+                                 lkspcpath);
+            return -1;
+        }
+    }
+
+    virLockDatabaseRemoveLockspace(db, lkspc);
+    virLockDatabaseLockspaceFree(lkspc);
+
+    return 0;
+}
+
+
+static virLockDatabaseLeasePtr
+virLockDatabaseLockspaceCreateLease(virLockDatabaseLockspacePtr lkspc,
+                                    const char *key)
+{
+    virLockDatabaseLeasePtr lease = NULL;
+    char *path = NULL;
+    int fd = -1;
+    int indexfd = -1;
+
+    VIR_DEBUG("lockspace=%p key=%s", lkspc, key);
+
+    if ((lease = virLockDatabaseLockspaceFindLease(lkspc, key)) != NULL) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("Lease %s in lockspace %s at %s already exists"),
+                     key, lkspc->name, lkspc->path);
+        return NULL;
+    }
+
+    if (!(path = virLockDatabaseBuildLeasePath(lkspc->path, lkspc->name, key)))
+        return NULL;
+
+    if ((indexfd = open(lkspc->idx->file.path, O_RDWR)) < 0) {
+        virReportSystemError(errno,
+                             _("Unable to open lease %s lockspace %s at %s"),
+                             lkspc->idx->key, lkspc->name, lkspc->path);
+        return NULL;
+    }
+
+    if (virFileLock(indexfd, false, 0, 1) < 0) {
+        virReportSystemError(errno,
+                             _("Unable to lock lease %s i lockspace %s at %s"),
+                             lkspc->idx->key, lkspc->name, lkspc->path);
+        goto error;
+    }
+
+    if (VIR_EXPAND_N(lkspc->leases, lkspc->nleases, 1) < 0) {
+        virReportOOMError();
+        goto error;
+    }
+
+    if (!(lease = virLockDatabaseLeaseNew(path, key))) {
+        VIR_SHRINK_N(lkspc->leases, lkspc->nleases, 1);
+        goto error;
+    }
+
+    VIR_DEBUG("Lease path %s", lease->file.path);
+
+    if ((fd = open(lease->file.path, O_RDWR|O_CREAT, 0600)) < 0) {
+        VIR_SHRINK_N(lkspc->leases, lkspc->nleases, 1);
+        virReportSystemError(errno,
+                             _("Unable to create lease %s lockspace %s at %s"),
+                             key, lkspc->name, lkspc->path);
+        goto error;
+    }
+    if (VIR_CLOSE(fd) < 0) {
+        VIR_SHRINK_N(lkspc->leases, lkspc->nleases, 1);
+        virReportSystemError(errno,
+                             _("Unable to save lease %s lockspace %s at %s"),
+                             key, lkspc->name, lkspc->path);
+        goto error;
+    }
+
+    if (VIR_CLOSE(indexfd) < 0) {
+        VIR_SHRINK_N(lkspc->leases, lkspc->nleases, 1);
+        virReportSystemError(errno,
+                             _("Unable to close lease %s lockspace %s at %s"),
+                             lkspc->idx->key, lkspc->name, lkspc->path);
+        goto error;
+    }
+
+    lkspc->leases[lkspc->nleases-1] = lease;
+
+    return lease;
+
+error:
+    virLockDatabaseLeaseFree(lease);
+    if (fd != -1)
+        unlink(path);
+    VIR_FORCE_CLOSE(fd);
+    VIR_FORCE_CLOSE(indexfd);
+    VIR_FREE(path);
+    return NULL;
+}
+
+
+static int
+virLockDatabaseLockspaceDeleteLease(virLockDatabaseLockspacePtr lkspc,
+                                    const char *key)
+{
+    virLockDatabaseLeasePtr lease;
+    int fd = -1;
+    int indexfd = -1;
+
+    VIR_DEBUG("lockspace=%p key=%s", lkspc, key);
+
+    if (!(lease = virLockDatabaseLockspaceFindLease(lkspc, key))) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("Lease %s in lockspace %s at %s does not exist"),
+                     key, lkspc->name, lkspc->path);
+        return -1;
+    }
+
+    if ((indexfd = open(lkspc->idx->file.path, O_RDWR)) < 0) {
+        virReportSystemError(errno,
+                             _("Unable to open lease %s lockspace %s at %s"),
+                             lkspc->idx->key, lkspc->name, lkspc->path);
+        return -1;
+    }
+
+    if (virFileLock(indexfd, false, 0, 1) < 0) {
+        virReportSystemError(errno,
+                             _("Unable to lock lease %s i lockspace %s at %s"),
+                             lkspc->idx->key, lkspc->name, lkspc->path);
+        goto error;
+    }
+
+    VIR_DEBUG("Lease path %s", lease->file.path);
+
+    if ((fd = open(lease->file.path, O_RDWR, 0600)) < 0) {
+        if (errno == ENOENT)
+            goto cleanup;
+
+        virReportSystemError(errno,
+                             _("Unable to create lease %s lockspace %s at %s"),
+                             key, lkspc->name, lkspc->path);
+        goto error;
+    }
+
+    if (unlink(lease->file.path) < 0) {
+        if (errno == ENOENT)
+            goto cleanup;
+
+        virReportSystemError(errno,
+                             _("Unable to delete lease %s lockspace %s at %s"),
+                             key, lkspc->name, lkspc->path);
+        return -1;
+    }
+
+cleanup:
+    if (VIR_CLOSE(fd) < 0) {
+        virReportSystemError(errno,
+                             _("Unable to closee lease %s lockspace %s at %s"),
+                             key, lkspc->name, lkspc->path);
+        goto error;
+    }
+
+    if (VIR_CLOSE(indexfd) < 0) {
+        virReportSystemError(errno,
+                             _("Unable to close lease %s lockspace %s at %s"),
+                             lkspc->idx->key, lkspc->name, lkspc->path);
+        goto error;
+    }
+
+    ignore_value(virLockDatabaseRemoveLease(lkspc, lease));
+
+    virLockDatabaseLeaseFree(lease);
+    return 0;
+
+error:
+    VIR_FORCE_CLOSE(fd);
+    VIR_FORCE_CLOSE(indexfd);
+    return -1;
+}
+
+
+int virLockDatabaseCreateLease(virLockDatabasePtr db,
+                               const char *path,
+                               const char *lockspace,
+                               const char *key)
+{
+    virLockDatabaseLockspacePtr lkspc;
+    virLockDatabaseLeasePtr lease;
+
+    VIR_DEBUG("db=%p path=%s lockspace=%s key=%s", db, path, lockspace, key);
+
+    if (!(lkspc = virLockDatabaseFindLockspace(db, lockspace))) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("Lockspace %s at %s does not exist"),
+                     lockspace, path);
+        return -1;
+    }
+
+    if (STRNEQ(path, lkspc->path)) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("Incorrect path %s for lockspace %s at %s"),
+                     path, lockspace, lkspc->path);
+        return -1;
+    }
+
+    if (!(lease = virLockDatabaseLockspaceCreateLease(lkspc, key)))
+        return -1;
+
+    return 0;
+}
+
+
+int virLockDatabaseDeleteLease(virLockDatabasePtr db,
+                               const char *path,
+                               const char *lockspace,
+                               const char *key)
+{
+    virLockDatabaseLockspacePtr lkspc;
+
+    VIR_DEBUG("db=%p path=%s lockspace=%s key=%s", db, path, lockspace, key);
+
+    if (!(lkspc = virLockDatabaseFindLockspace(db, lockspace))) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("Lockspace %s at %s does not exist"),
+                     lockspace, path);
+        return -1;
+    }
+
+
+    if (STRNEQ(path, lkspc->path)) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("Incorrect path %s for lockspace %s at %s"),
+                     path, lockspace, lkspc->path);
+        return -1;
+    }
+
+    return virLockDatabaseLockspaceDeleteLease(lkspc, key);
+}
+
+
+int virLockDatabaseAcquireLease(virLockDatabasePtr db,
+                                const char *path,
+                                const char *lockspace,
+                                const char *key,
+                                bool shared)
+{
+    int ret = -1;
+    virLockDatabaseLockspacePtr lkspc = NULL;
+    virLockDatabaseLeasePtr lease = NULL;
+    VIR_DEBUG("db=%p path=%s lockspace=%s key=%s shared=%d",
+              db, path, lockspace, key, shared);
+
+    if (!(lkspc = virLockDatabaseFindLockspace(db, lockspace))) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("Lockspace %s at %s does not exist"),
+                     lockspace, path);
+        return -1;
+    }
+
+    if (STRNEQ(lkspc->path, path)) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("Incorrect path %s for lockspace %s at %s"),
+                     path, lockspace, lkspc->path);
+        return -1;
+    }
+
+    if (!(lease = virLockDatabaseLockspaceFindLease(lkspc, key))) {
+        if (!lkspc->autoLease) {
+            virLockError(VIR_ERR_INTERNAL_ERROR,
+                         _("Lease %s in lockspace %s at %s does not exist"),
+                         key, lockspace, path);
+            return -1;
+        }
+
+        if (!(lease = virLockDatabaseLockspaceCreateLease(lkspc, key)))
+            return -1;
+    }
+
+    VIR_DEBUG("Lease key=%s fd=%d shared=%d refs=%d",
+              lease->key, lease->file.fd, lease->file.shared, lease->file.refs);
+
+
+    if (lease->file.fd == -1) {
+        /* No one holds it locally. Try and grab the fcntl lock
+         * to stop other hosts
+         */
+        if ((lease->file.fd = open(lease->file.path, O_RDWR)) < 0) {
+            virReportSystemError(errno,
+                                 _("Unable to open lease %s i lockspace %s at %s"),
+                                 key, lockspace, path);
+            goto cleanup;
+        }
+
+        if (virFileLock(lease->file.fd, shared, 0, 1) < 0) {
+            virReportSystemError(errno,
+                                 _("Unable to lock lease %s i lockspace %s at %s"),
+                                 key, lockspace, path);
+            VIR_FORCE_CLOSE(lease->file.fd);
+            goto cleanup;
+        }
+
+        lease->file.shared = shared;
+        lease->file.refs = 1;
+    } else {
+        /* Someone on this host already holds the lock, so
+         * check if sharing mode is compatible */
+        if (!lease->file.shared || !shared) {
+            virLockError(VIR_ERR_RESOURCE_BUSY,
+                         _("Lease %s in lockspace %s at %s is already held"),
+                         key, lockspace, path);
+            goto cleanup;
+        }
+
+        /* Both shared, so claim another ref */
+        lease->file.refs++;
+    }
+
+    ret = 0;
+
+cleanup:
+    if (ret != 0) {
+        if (lease &&
+            lease->file.fd == -1 &&
+            lkspc->autoLease) {
+            virErrorPtr orig_error = virSaveLastError();
+
+            virLockDatabaseLockspaceDeleteLease(lkspc, key);
+
+            if (orig_error) {
+                virSetError(orig_error);
+                virFreeError(orig_error);
+            }
+        }
+
+    }
+    return ret;
+}
+
+
+int virLockDatabaseReleaseLease(virLockDatabasePtr db,
+                                const char *path,
+                                const char *lockspace,
+                                const char *key,
+                                bool shared)
+{
+    int ret = -1;
+    virLockDatabaseLockspacePtr lkspc = NULL;
+    virLockDatabaseLeasePtr lease = NULL;
+
+    VIR_DEBUG("db=%p path=%s lockspace=%s key=%s shared=%d",
+              db, path, lockspace, key, shared);
+
+    if (!(lkspc = virLockDatabaseFindLockspace(db, lockspace))) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("Lockspace %s at %s does not exist"),
+                     lockspace, path);
+        return -1;
+    }
+
+    if (STRNEQ(lkspc->path, path)) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("Incorrect path %s for lockspace %s at %s"),
+                     path, lockspace, lkspc->path);
+        return -1;
+    }
+
+    if (!(lease = virLockDatabaseLockspaceFindLease(lkspc, key))) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("Lease %s in lockspace %s at %s does not exist"),
+                     key, lockspace, path);
+        return -1;
+    }
+
+    VIR_DEBUG("Lease key=%s fd=%d shared=%d refs=%d",
+              lease->key, lease->file.fd, lease->file.shared, lease->file.refs);
+
+    if (lease->file.fd == -1) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("Lease %s in lockspace %s at %s is not held"),
+                     key, lockspace, path);
+        return -1;
+    }
+
+    /* Someone on this host already holds the lock, so
+     * check if sharing mode is compatible */
+    if (lease->file.shared) {
+        if (!shared) {
+            virLockError(VIR_ERR_RESOURCE_BUSY,
+                         _("Lease %s in lockspace %s at %s is shared, not exclusive"),
+                         key, lockspace, path);
+            return -1;
+        }
+        lease->file.refs--;
+    } else {
+        if (shared) {
+            virLockError(VIR_ERR_RESOURCE_BUSY,
+                         _("Lease %s in lockspace %s at %s is exclusive, not shared"),
+                         key, lockspace, path);
+            return -1;
+        }
+    }
+
+    if (!lease->file.shared || (lease->file.refs == 0)) {
+        if (virFileUnlock(lease->file.fd, 0, 1) < 0) {
+            char ebuf[1024];
+            VIR_ERROR(_("Unable to unlock lease %s i lockspace %s at %s: %s"),
+                      key, lockspace, path,
+                      virStrerror(errno, ebuf, sizeof(ebuf)));
+        }
+        VIR_FORCE_CLOSE(lease->file.fd);
+        virLockDatabaseRemoveLease(lkspc, lease);
+        virLockDatabaseLeaseFree(lease);
+    }
+
+    ret = 0;
+
+    return ret;
+}
diff --git a/src/locking/lock_database.h b/src/locking/lock_database.h
new file mode 100644
index 0000000..3480383
--- /dev/null
+++ b/src/locking/lock_database.h
@@ -0,0 +1,43 @@
+
+
+#include "internal.h"
+
+typedef struct _virLockDatabase virLockDatabase;
+typedef virLockDatabase *virLockDatabasePtr;
+
+
+virLockDatabasePtr virLockDatabaseNew(const unsigned char *hostuuid,
+                                      const char *hostname);
+
+void virLockDatabaseFree(virLockDatabasePtr db);
+
+int virLockDatabaseCreateLockspace(virLockDatabasePtr db,
+                                   const char *path,
+                                   const char *name,
+                                   bool autoLease);
+
+int virLockDatabaseDeleteLockspace(virLockDatabasePtr db,
+                                   const char *path,
+                                   const char *name);
+
+int virLockDatabaseCreateLease(virLockDatabasePtr db,
+                               const char *path,
+                               const char *lockspace,
+                               const char *key);
+
+int virLockDatabaseDeleteLease(virLockDatabasePtr db,
+                               const char *path,
+                               const char *lockspace,
+                               const char *key);
+
+int virLockDatabaseAcquireLease(virLockDatabasePtr db,
+                                const char *path,
+                                const char *lockspace,
+                                const char *key,
+                                bool shared);
+
+int virLockDatabaseReleaseLease(virLockDatabasePtr db,
+                                const char *path,
+                                const char *lockspace,
+                                const char *key,
+                                bool shared);
diff --git a/src/util/virterror.c b/src/util/virterror.c
index 75058f3..ff93109 100644
--- a/src/util/virterror.c
+++ b/src/util/virterror.c
@@ -1174,6 +1174,12 @@ virErrorMsg(virErrorNumber error, const char *info)
             else
                 errmsg = _("invalid stream pointer in %s");
             break;
+        case VIR_ERR_RESOURCE_BUSY:
+            if (info == NULL)
+                errmsg = _("resource busy");
+            else
+                errmsg = _("resource busy %s");
+            break;
     }
     return (errmsg);
 }
-- 
1.7.6




More information about the libvir-list mailing list