From: "Daniel P. Berrange" <berrange(a)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