From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list