[libvirt] [PATCH 01/12] Introduce an internal API for handling file based lockspaces
Michal Privoznik
mprivozn at redhat.com
Fri Oct 5 11:26:00 UTC 2012
On 12.09.2012 18:28, Daniel P. Berrange wrote:
> From: "Daniel P. Berrange" <berrange at redhat.com>
>
> The previously introduced virFile{Lock,Unlock} APIs provide a
> way to acquire/release fcntl() locks on individual files. For
> unknown reason though, the POSIX spec says that fcntl() locks
> are released when *any* file handle referring to the same path
> is closed. In the following sequence
>
> threadA: fd1 = open("foo")
> threadB: fd2 = open("foo")
> threadA: virFileLock(fd1)
> threadB: virFileLock(fd2)
> threadB: close(fd2)
>
> you'd expect threadA to come out holding a lock on 'foo', and
> indeed it does hold a lock for a very short time. Unfortunately
> when threadB does close(fd2) this releases the lock associated
> with fd1. For the current libvirt use case for virFileLock -
> pidfiles - this doesn't matter since the lock is acquired
> at startup while single threaded an never released until
> exit.
>
> To provide a more generally useful API though, it is necessary
> to introduce a slightly higher level abstraction, which is to
> be referred to as a "lockspace". This is to be provided by
> a virLockSpacePtr object in src/util/virlockspace.{c,h}. The
> core idea is that the lockspace keeps track of what files are
> already open+locked. This means that when a 2nd thread comes
> along and tries to acquire a lock, it doesn't end up opening
> and closing a new FD. The lockspace just checks the current
> list of held locks and immediately returns VIR_ERR_RESOURCE_BUSY.
>
> NB, the API as it stands is designed on the basis that the
> files being locked are not being otherwise opened and used
> by the application code. One approach to using this API is to
> acquire locks based on a hash of the filepath.
>
> eg to lock /var/lib/libvirt/images/foo.img the application
> might do
>
> virLockSpacePtr lockspace = virLockSpaceNew("/var/lib/libvirt/imagelocks");
> lockname = md5sum("/var/lib/libvirt/images/foo.img");
> virLockSpaceAcquireLock(lockspace, lockname);
>
> NB, in this example, the caller should ensure that the path
> is canonicalized before calculating the checksum.
>
> It is also possible to do locks directly on resources by
> using a NULL lockspace directory and then using the file
> path as the lock name eg
>
> virLockSpacePtr lockspace = virLockSpaceNew(NULL);
> virLockSpaceAcquireLock(lockspace, "/var/lib/libvirt/images/foo.img");
>
> This is only safe to do though if no other part of the process
> will be opening the files. This will be the case when this
> code is used inside the soon-to-be-reposted virlockd daemon
>
> Signed-off-by: Daniel P. Berrange <berrange at redhat.com>
> ---
> .gitignore | 1 +
> include/libvirt/virterror.h | 4 +-
> po/POTFILES.in | 1 +
> src/Makefile.am | 1 +
> src/libvirt_private.syms | 11 +
> src/util/virlockspace.c | 558 ++++++++++++++++++++++++++++++++++++++++++++
> src/util/virlockspace.h | 58 +++++
> src/util/virterror.c | 9 +-
> tests/Makefile.am | 7 +-
> tests/virlockspacetest.c | 363 ++++++++++++++++++++++++++++
> 10 files changed, 1010 insertions(+), 3 deletions(-)
> create mode 100644 src/util/virlockspace.c
> create mode 100644 src/util/virlockspace.h
> create mode 100644 tests/virlockspacetest.c
>
ACK but see my comments below.
> diff --git a/.gitignore b/.gitignore
> index d998f0e..7919f74 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -161,6 +161,7 @@
> /tests/virdrivermoduletest
> /tests/virhashtest
> /tests/virkeyfiletest
> +/tests/virlockspacetest
> /tests/virnet*test
> /tests/virshtest
> /tests/virtimetest
> diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
> index 5140c38..98c2cb2 100644
> --- a/include/libvirt/virterror.h
> +++ b/include/libvirt/virterror.h
> @@ -112,7 +112,8 @@ typedef enum {
> VIR_FROM_PARALLELS = 48, /* Error from Parallels */
> VIR_FROM_DEVICE = 49, /* Error from Device */
>
> - VIR_FROM_SSH = 50, /* Error from libssh2 connection transport */
> + VIR_FROM_SSH = 50, /* Error from libssh2 connection transport */
> + VIR_FROM_LOCKSPACE = 51, /* Error from lockspace */
>
> # ifdef VIR_ENUM_SENTINELS
> VIR_ERR_DOMAIN_LAST
> @@ -285,6 +286,7 @@ typedef enum {
> VIR_ERR_SSH = 85, /* error in ssh transport driver */
> VIR_ERR_AGENT_UNRESPONSIVE = 86, /* guest agent is unresponsive,
> not running or not usable */
> + VIR_ERR_RESOURCE_BUSY = 87, /* resource is already in use */
> } virErrorNumber;
>
> /**
> diff --git a/po/POTFILES.in b/po/POTFILES.in
> index 7a91eb4..c5f4cf7 100644
> --- a/po/POTFILES.in
> +++ b/po/POTFILES.in
> @@ -148,6 +148,7 @@ src/util/virdbus.c
> src/util/virfile.c
> src/util/virhash.c
> src/util/virkeyfile.c
> +src/util/virlockspace.c
> src/util/virnetdev.c
> src/util/virnetdevbridge.c
> src/util/virnetdevmacvlan.c
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 9f27fcf..6860e7f 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -96,6 +96,7 @@ UTIL_SOURCES = \
> util/virkeycode.c util/virkeycode.h \
> util/virkeyfile.c util/virkeyfile.h \
> util/virkeymaps.h \
> + util/virlockspace.c util/virlockspace.h \
> util/virmacaddr.h util/virmacaddr.c \
> util/virnetdev.h util/virnetdev.c \
> util/virnetdevbandwidth.h util/virnetdevbandwidth.c \
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index 0494e1f..58a9520 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -1321,6 +1321,17 @@ virKeyFileHasGroup;
> virKeyFileGetValueString;
>
>
> +# virlockspace.h
> +virLockSpaceAcquireResource;
> +virLockSpaceCreateResource;
> +virLockSpaceDeleteResource;
> +virLockSpaceFree;
> +virLockSpaceGetDirectory;
> +virLockSpaceNew;
> +virLockSpaceReleaseResource;
> +virLockSpaceReleaseResourcesForOwner;
> +
> +
> # virmacaddr.h
> virMacAddrCmp;
> virMacAddrCmpRaw;
> diff --git a/src/util/virlockspace.c b/src/util/virlockspace.c
> new file mode 100644
> index 0000000..611592a
> --- /dev/null
> +++ b/src/util/virlockspace.c
> @@ -0,0 +1,558 @@
> +/*
> + * virlockspace.c: simple file based lockspaces
> + *
> + * Copyright (C) 2012 Red Hat, Inc.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; If not, see
> + * <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#include <config.h>
> +
> +#include "virlockspace.h"
> +#include "logging.h"
> +#include "memory.h"
> +#include "virterror_internal.h"
> +#include "util.h"
> +#include "virfile.h"
> +#include "virhash.h"
> +#include "threads.h"
> +
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <sys/stat.h>
> +
> +#define VIR_FROM_THIS VIR_FROM_LOCKSPACE
> +
> +typedef struct _virLockSpaceResource virLockSpaceResource;
> +typedef virLockSpaceResource *virLockSpaceResourcePtr;
> +
> +struct _virLockSpaceResource {
> + char *name;
> + char *path;
> + int fd;
> + bool lockHeld;
> + unsigned int flags;
> + size_t nOwners;
> + pid_t *owners;
> +};
> +
> +struct _virLockSpace {
> + char *dir;
> + virMutex lock;
> +
> + virHashTablePtr resources;
> +};
> +
> +
> +static char *virLockSpaceGetResourcePath(virLockSpacePtr lockspace,
> + const char *resname)
> +{
> + char *ret;
> + if (lockspace->dir) {
> + if (virAsprintf(&ret, "%s/%s", lockspace->dir, resname) < 0) {
> + virReportOOMError();
> + return NULL;
> + }
> + } else {
> + if (!(ret = strdup(resname))) {
> + virReportOOMError();
> + return NULL;
> + }
> + }
> +
> + return ret;
> +}
> +
> +
> +static void virLockSpaceResourceFree(virLockSpaceResourcePtr res)
> +{
> + if (!res)
> + return;
> +
> + if (res->lockHeld &&
> + (res->flags & VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE)) {
> + if (res->flags & VIR_LOCK_SPACE_ACQUIRE_SHARED) {
> + /* We must upgrade to an exclusive lock to ensure
> + * no one else still has it before trying to delete */
> + if (virFileLock(res->fd, false, 0, 1) < 0) {
> + VIR_DEBUG("Could not upgrade shared lease to exclusive, not deleting");
> + } else {
> + if (unlink(res->path) < 0 &&
> + errno != ENOENT) {
> + char ebuf[1024];
> + VIR_WARN("Failed to unlink resource %s: %s",
> + res->path, virStrerror(errno, ebuf, sizeof(ebuf)));
> + }
> + }
> + } else {
> + if (unlink(res->path) < 0 &&
> + errno != ENOENT) {
> + char ebuf[1024];
> + VIR_WARN("Failed to unlink resource %s: %s",
> + res->path, virStrerror(errno, ebuf, sizeof(ebuf)));
> + }
> + }
> + }
> +
> + VIR_FORCE_CLOSE(res->fd);
> + VIR_FREE(res->path);
> + VIR_FREE(res->name);
> + VIR_FREE(res);
> +}
> +
> +
> +static virLockSpaceResourcePtr
> +virLockSpaceResourceNew(virLockSpacePtr lockspace,
> + const char *resname,
> + unsigned int flags,
> + pid_t owner)
> +{
> + virLockSpaceResourcePtr res;
> + bool shared = !!(flags & VIR_LOCK_SPACE_ACQUIRE_SHARED);
> +
> + if (VIR_ALLOC(res) < 0)
> + return NULL;
> +
> + res->fd = -1;
> + res->flags = flags;
> +
> + if (!(res->name = strdup(resname)))
> + goto no_memory;
> +
> + if (!(res->path = virLockSpaceGetResourcePath(lockspace, resname)))
> + goto no_memory;
> +
> + if (flags & VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE) {
> + while (1) {
> + struct stat a, b;
> + if ((res->fd = open(res->path, O_RDWR|O_CREAT, 0600)) < 0) {
> + virReportSystemError(errno,
> + _("Unable to open/create resource %s"),
> + res->path);
> + goto error;
> + }
> +
> + if (virSetCloseExec(res->fd) < 0) {
> + virReportSystemError(errno,
> + _("Failed to set close-on-exec flag '%s'"),
> + res->path);
> + goto error;
> + }
> +
> + if (fstat(res->fd, &b) < 0) {
> + virReportSystemError(errno,
> + _("Unable to check status of pid file '%s'"),
> + res->path);
> + goto error;
> + }
> +
> + if (virFileLock(res->fd, shared, 0, 1) < 0) {
> + if (errno == EACCES || errno == EAGAIN) {
> + virReportError(VIR_ERR_RESOURCE_BUSY,
> + _("Lockspace resource '%s' is locked"),
> + resname);
> + } else {
> + virReportSystemError(errno,
> + _("Unable to acquire lock on '%s'"),
> + res->path);
> + }
> + goto error;
> + }
> +
> + /* Now make sure the pidfile we locked is the same
> + * one that now exists on the filesystem
> + */
> + if (stat(res->path, &a) < 0) {
> + char ebuf[1024] ATTRIBUTE_UNUSED;
> + VIR_DEBUG("Resource '%s' disappeared: %s",
> + res->path, virStrerror(errno, ebuf, sizeof(ebuf)));
> + VIR_FORCE_CLOSE(res->fd);
> + /* Someone else must be racing with us, so try again */
> + continue;
> + }
> +
> + if (a.st_ino == b.st_ino)
> + break;
> +
> + VIR_DEBUG("Resource '%s' was recreated", res->path);
> + VIR_FORCE_CLOSE(res->fd);
> + /* Someone else must be racing with us, so try again */
> + }
> + } else {
> + if ((res->fd = open(res->path, O_RDWR)) < 0) {
> + virReportSystemError(errno,
> + _("Unable to open resource %s"),
> + res->path);
> + goto error;
> + }
> +
> + if (virSetCloseExec(res->fd) < 0) {
> + virReportSystemError(errno,
> + _("Failed to set close-on-exec flag '%s'"),
> + res->path);
> + goto error;
> + }
> +
> + if (virFileLock(res->fd, !!(flags & VIR_LOCK_SPACE_ACQUIRE_SHARED), 0, 0) < 0) {
I think you can s/!!(...)/shared/
> + if (errno == EACCES || errno == EAGAIN) {
> + virReportError(VIR_ERR_RESOURCE_BUSY,
> + _("Lockspace resource '%s' is locked"),
> + resname);
> + } else {
> + virReportSystemError(errno,
> + _("Unable to acquire lock on '%s'"),
> + res->path);
> + }
> + goto error;
> + }
> + }
> + res->lockHeld = true;
> +
> + if (VIR_EXPAND_N(res->owners, res->nOwners, 1) < 0)
> + goto no_memory;
> +
> + res->owners[res->nOwners-1] = owner;
> +
> + return res;
> +
> +no_memory:
> + virReportOOMError();
> +error:
> + virLockSpaceResourceFree(res);
> + return NULL;
> +}
> +
> +
> +static void virLockSpaceResourceDataFree(void *opaque, const void *name ATTRIBUTE_UNUSED)
> +{
> + virLockSpaceResourcePtr res = opaque;
> + virLockSpaceResourceFree(res);
> +}
> +
> +
> +virLockSpacePtr virLockSpaceNew(const char *directory)
> +{
> + virLockSpacePtr lockspace;
> +
> + VIR_DEBUG("directory=%s", NULLSTR(directory));
> +
> + if (VIR_ALLOC(lockspace) < 0)
> + return NULL;
> +
> + if (virMutexInit(&lockspace->lock) < 0) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("Unable to initialize lockspace mutex"));
> + VIR_FREE(lockspace);
> + return NULL;
> + }
> +
> + if (directory &&
> + !(lockspace->dir = strdup(directory)))
> + goto no_memory;
> +
> + if (!(lockspace->resources = virHashCreate(10,
> + virLockSpaceResourceDataFree)))
10 is a magic constant esp. when used in subsequent patches.
I think you can
#define VIR_LOCK_SPACE_RESOURCE_HASH_TABLE_SIZE 10
or something.
> + goto error;
> +
> + if (directory) {
> + if (virFileExists(directory)) {
> + if (!virFileIsDir(directory)) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("Lockspace location %s exists, but is not a directory"),
> + directory);
> + goto error;
> + }
> + } else {
> + if (virFileMakePathWithMode(directory, 0700) < 0) {
> + virReportSystemError(errno,
> + _("Unable to create lockspace %s"),
> + directory);
> + goto error;
> + }
> + }
> + }
> +
> + return lockspace;
> +
> +no_memory:
> + virReportOOMError();
> +error:
> + virLockSpaceFree(lockspace);
> + return NULL;
> +}
> +
> +
> +void virLockSpaceFree(virLockSpacePtr lockspace)
> +{
> + if (!lockspace)
> + return;
> +
> + virHashFree(lockspace->resources);
> + VIR_FREE(lockspace->dir);
> + virMutexDestroy(&lockspace->lock);
> + VIR_FREE(lockspace);
> +}
> +
> +
> +const char *virLockSpaceGetDirectory(virLockSpacePtr lockspace)
> +{
> + return lockspace->dir;
> +}
> +
> +
> +int virLockSpaceCreateResource(virLockSpacePtr lockspace,
> + const char *resname)
> +{
> + int ret = -1;
> + char *respath = NULL;
> + virLockSpaceResourcePtr res;
> +
> + VIR_DEBUG("lockspace=%p resname=%s", lockspace, resname);
> +
> + virMutexLock(&lockspace->lock);
> +
> + if ((res = virHashLookup(lockspace->resources, resname))) {
> + virReportError(VIR_ERR_RESOURCE_BUSY,
> + _("Lockspace resource '%s' is locked"),
> + resname);
> + goto cleanup;
> + }
> +
> + if (!(respath = virLockSpaceGetResourcePath(lockspace, resname)))
> + goto cleanup;
> +
> + if (virFileTouch(respath, 0600) < 0)
> + goto cleanup;
> +
> + ret = 0;
> +
> +cleanup:
> + virMutexUnlock(&lockspace->lock);
> + VIR_FREE(respath);
> + return ret;
> +}
> +
> +
> +int virLockSpaceDeleteResource(virLockSpacePtr lockspace,
> + const char *resname)
> +{
> + int ret = -1;
> + char *respath = NULL;
> + virLockSpaceResourcePtr res;
> +
> + VIR_DEBUG("lockspace=%p resname=%s", lockspace, resname);
> +
> + virMutexLock(&lockspace->lock);
> +
> + if ((res = virHashLookup(lockspace->resources, resname))) {
> + virReportError(VIR_ERR_RESOURCE_BUSY,
> + _("Lockspace resource '%s' is locked"),
> + resname);
> + goto cleanup;
> + }
> +
> + if (!(respath = virLockSpaceGetResourcePath(lockspace, resname)))
> + goto cleanup;
> +
> + if (unlink(respath) < 0 &&
> + errno != ENOENT) {
> + virReportSystemError(errno,
> + _("Unable to delete lockspace resource %s"),
> + respath);
> + goto cleanup;
> + }
> +
> + ret = 0;
> +
> +cleanup:
> + virMutexUnlock(&lockspace->lock);
> + VIR_FREE(respath);
> + return ret;
> +}
> +
> +
> +int virLockSpaceAcquireResource(virLockSpacePtr lockspace,
> + const char *resname,
> + pid_t owner,
> + unsigned int flags)
> +{
> + int ret = -1;
> + virLockSpaceResourcePtr res;
> +
> + VIR_DEBUG("lockspace=%p resname=%s flags=%x owner=%lld",
> + lockspace, resname, flags, (unsigned long long)owner);
> +
> + virCheckFlags(VIR_LOCK_SPACE_ACQUIRE_SHARED |
> + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE, -1);
> +
> + virMutexLock(&lockspace->lock);
> +
> + if ((res = virHashLookup(lockspace->resources, resname))) {
> + if ((res->flags & VIR_LOCK_SPACE_ACQUIRE_SHARED) &&
> + (flags & VIR_LOCK_SPACE_ACQUIRE_SHARED)) {
> +
> + if (VIR_EXPAND_N(res->owners, res->nOwners, 1) < 0) {
> + virReportOOMError();
> + goto cleanup;
> + }
> + res->owners[res->nOwners-1] = owner;
> +
> + goto done;
> + }
> + virReportError(VIR_ERR_RESOURCE_BUSY,
> + _("Lockspace resource '%s' is locked"),
> + resname);
> + goto cleanup;
> + }
> +
> + if (!(res = virLockSpaceResourceNew(lockspace, resname, flags, owner)))
> + goto cleanup;
> +
> + if (virHashAddEntry(lockspace->resources, resname, res) < 0) {
> + virLockSpaceResourceFree(res);
> + goto cleanup;
> + }
> +
> +done:
> + ret = 0;
> +
> +cleanup:
> + virMutexUnlock(&lockspace->lock);
> + return ret;
> +}
> +
> +
> +int virLockSpaceReleaseResource(virLockSpacePtr lockspace,
> + const char *resname,
> + pid_t owner)
> +{
> + int ret = -1;
> + virLockSpaceResourcePtr res;
> + size_t i;
> +
> + VIR_DEBUG("lockspace=%p resname=%s owner=%lld",
> + lockspace, resname, (unsigned long long)owner);
> +
> + virMutexLock(&lockspace->lock);
> +
> + if (!(res = virHashLookup(lockspace->resources, resname))) {
> + virReportError(VIR_ERR_RESOURCE_BUSY,
> + _("Lockspace resource '%s' is not locked"),
> + resname);
> + goto cleanup;
> + }
> +
> + for (i = 0 ; i < res->nOwners ; i++) {
> + if (res->owners[i] == owner) {
> + break;
> + }
> + }
> +
> + if (i == res->nOwners) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("owner %lld does not hold the resource lock"),
> + (unsigned long long)owner);
> + goto cleanup;
> + }
> +
> + if (i < (res->nOwners - 1))
> + memmove(res->owners + i,
> + res->owners + i + 1,
> + (res->nOwners - i - 1) * sizeof(res->owners[0]));
> + VIR_SHRINK_N(res->owners, res->nOwners, 1);
> +
> + if ((res->nOwners == 0) &&
> + virHashRemoveEntry(lockspace->resources, resname) < 0)
> + goto cleanup;
> +
> + ret = 0;
> +
> +cleanup:
> + virMutexUnlock(&lockspace->lock);
> + return ret;
> +}
> +
> +
> +struct virLockSpaceRemoveData {
> + pid_t owner;
> + size_t count;
> +};
> +
> +
> +static int
> +virLockSpaceRemoveResourcesForOwner(const void *payload,
> + const void *name ATTRIBUTE_UNUSED,
> + const void *opaque)
> +{
> + virLockSpaceResourcePtr res = (virLockSpaceResourcePtr)payload;
> + struct virLockSpaceRemoveData *data = (struct virLockSpaceRemoveData *)opaque;
> + size_t i;
> +
> + VIR_DEBUG("res %s owner %lld", res->name, (unsigned long long)data->owner);
> +
> + for (i = 0 ; i < res->nOwners ; i++) {
> + if (res->owners[i] == data->owner) {
> + break;
> + }
> + }
> +
> + if (i == res->nOwners)
> + return 0;
> +
> + data->count++;
> +
> + if (i < (res->nOwners - 1))
> + memmove(res->owners + i,
> + res->owners + i + 1,
> + (res->nOwners - i - 1) * sizeof(res->owners[0]));
> + VIR_SHRINK_N(res->owners, res->nOwners, 1);
> +
> + if (res->nOwners) {
> + VIR_DEBUG("Other shared owners remain");
> + return 0;
> + }
> +
> + VIR_DEBUG("No more owners, remove it");
> + return 1;
> +}
> +
> +
> +int virLockSpaceReleaseResourcesForOwner(virLockSpacePtr lockspace,
> + pid_t owner)
> +{
> + int ret = 0;
> + struct virLockSpaceRemoveData data = {
> + owner, 0
> + };
> +
> + VIR_DEBUG("lockspace=%p owner=%lld", lockspace, (unsigned long long)owner);
> +
> + virMutexLock(&lockspace->lock);
> +
> + if (virHashRemoveSet(lockspace->resources,
> + virLockSpaceRemoveResourcesForOwner,
> + &data) < 0)
> + goto error;
> +
> + ret = data.count;
> +
> + virMutexUnlock(&lockspace->lock);
> + return ret;
> +
> +error:
> + virMutexUnlock(&lockspace->lock);
> + return -1;
> +}
> diff --git a/src/util/virlockspace.h b/src/util/virlockspace.h
> new file mode 100644
> index 0000000..bd8f91c
> --- /dev/null
> +++ b/src/util/virlockspace.h
> @@ -0,0 +1,58 @@
> +/*
> + * virlockspace.h: simple file based lockspaces
> + *
> + * Copyright (C) 2012 Red Hat, Inc.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; If not, see
> + * <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#ifndef __VIR_LOCK_SPACE_H__
> +# define __VIR_LOCK_SPACE_H__
> +
> +# include "internal.h"
> +
> +typedef struct _virLockSpace virLockSpace;
> +typedef virLockSpace *virLockSpacePtr;
> +
> +virLockSpacePtr virLockSpaceNew(const char *directory);
> +
> +void virLockSpaceFree(virLockSpacePtr lockspace);
> +
> +const char *virLockSpaceGetDirectory(virLockSpacePtr lockspace);
> +
> +int virLockSpaceCreateResource(virLockSpacePtr lockspace,
> + const char *resname);
> +int virLockSpaceDeleteResource(virLockSpacePtr lockspace,
> + const char *resname);
> +
> +typedef enum {
> + VIR_LOCK_SPACE_ACQUIRE_SHARED = (1 << 0),
> + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE = (1 << 1),
> +} virLockSpaceAcquireFlags;
> +
> +int virLockSpaceAcquireResource(virLockSpacePtr lockspace,
> + const char *resname,
> + pid_t owner,
> + unsigned int flags);
> +
> +int virLockSpaceReleaseResource(virLockSpacePtr lockspace,
> + const char *resname,
> + pid_t owner);
> +
> +int virLockSpaceReleaseResourcesForOwner(virLockSpacePtr lockspace,
> + pid_t owner);
> +
> +#endif /* __VIR_LOCK_SPACE_H__ */
> diff --git a/src/util/virterror.c b/src/util/virterror.c
> index 7caa69e..6fade3b 100644
> --- a/src/util/virterror.c
> +++ b/src/util/virterror.c
> @@ -115,7 +115,8 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST,
> "Parallels Cloud Server",
> "Device Config",
>
> - "SSH transport layer" /* 50 */
> + "SSH transport layer", /* 50 */
> + "Lock Space",
> )
>
>
> @@ -1206,6 +1207,12 @@ virErrorMsg(virErrorNumber error, const char *info)
> else
> errmsg = _("Guest agent is not responding: %s");
> break;
> + case VIR_ERR_RESOURCE_BUSY:
> + if (info == NULL)
> + errmsg = _("resource busy");
> + else
> + errmsg = _("resource busy %s");
> + break;
> }
> return errmsg;
> }
> diff --git a/tests/Makefile.am b/tests/Makefile.am
> index bec89e2..bcc2163 100644
> --- a/tests/Makefile.am
> +++ b/tests/Makefile.am
> @@ -92,7 +92,7 @@ test_programs = virshtest sockettest \
> viratomictest \
> utiltest virnettlscontexttest shunloadtest \
> virtimetest viruritest virkeyfiletest \
> - virauthconfigtest
> + virauthconfigtest virlockspacetest
>
> if WITH_SECDRIVER_SELINUX
> test_programs += securityselinuxtest
> @@ -534,6 +534,11 @@ virtimetest_SOURCES = \
> virtimetest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS)
> virtimetest_LDADD = $(LDADDS)
>
> +virlockspacetest_SOURCES = \
> + virlockspacetest.c testutils.h testutils.c
> +virlockspacetest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS)
> +virlockspacetest_LDADD = $(LDADDS)
> +
> viruritest_SOURCES = \
> viruritest.c testutils.h testutils.c
> viruritest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS)
> diff --git a/tests/virlockspacetest.c b/tests/virlockspacetest.c
> new file mode 100644
> index 0000000..ee58f95
> --- /dev/null
> +++ b/tests/virlockspacetest.c
> @@ -0,0 +1,363 @@
> +/*
> + * Copyright (C) 2011 Red Hat, Inc.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; If not, see
> + * <http://www.gnu.org/licenses/>.
> + *
> + * Author: Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +#include <config.h>
> +
> +#include <stdlib.h>
> +#include <signal.h>
> +#include <sys/stat.h>
> +
> +#include "testutils.h"
> +#include "util.h"
> +#include "virterror_internal.h"
> +#include "memory.h"
> +#include "logging.h"
> +
> +#include "virlockspace.h"
> +
> +#define VIR_FROM_THIS VIR_FROM_RPC
> +
> +#define LOCKSPACE_DIR abs_builddir "/virlockspacedata"
> +
> +static int testLockSpaceCreate(const void *args ATTRIBUTE_UNUSED)
> +{
> + virLockSpacePtr lockspace;
> + int ret = -1;
> +
> + rmdir(LOCKSPACE_DIR);
> +
> + lockspace = virLockSpaceNew(LOCKSPACE_DIR);
> +
> + if (!virFileIsDir(LOCKSPACE_DIR))
> + goto cleanup;
> +
> + ret = 0;
> +
> +cleanup:
> + virLockSpaceFree(lockspace);
> + rmdir(LOCKSPACE_DIR);
> + return ret;
> +}
> +
> +
> +static int testLockSpaceResourceLifecycle(const void *args ATTRIBUTE_UNUSED)
> +{
> + virLockSpacePtr lockspace;
> + int ret = -1;
> +
> + rmdir(LOCKSPACE_DIR);
> +
> + lockspace = virLockSpaceNew(LOCKSPACE_DIR);
> +
> + if (!virFileIsDir(LOCKSPACE_DIR))
> + goto cleanup;
> +
> + if (virLockSpaceCreateResource(lockspace, "foo") < 0)
> + goto cleanup;
> +
> + if (!virFileExists(LOCKSPACE_DIR "/foo"))
> + goto cleanup;
> +
> + if (virLockSpaceDeleteResource(lockspace, "foo") < 0)
> + goto cleanup;
> +
> + if (virFileExists(LOCKSPACE_DIR "/foo"))
> + goto cleanup;
> +
> + ret = 0;
> +
> +cleanup:
> + virLockSpaceFree(lockspace);
> + rmdir(LOCKSPACE_DIR);
> + return ret;
> +}
> +
> +
> +static int testLockSpaceResourceLockExcl(const void *args ATTRIBUTE_UNUSED)
> +{
> + virLockSpacePtr lockspace;
> + int ret = -1;
> +
> + rmdir(LOCKSPACE_DIR);
> +
> + lockspace = virLockSpaceNew(LOCKSPACE_DIR);
> +
> + if (!virFileIsDir(LOCKSPACE_DIR))
> + goto cleanup;
> +
> + if (virLockSpaceCreateResource(lockspace, "foo") < 0)
> + goto cleanup;
> +
> + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), 0) < 0)
> + goto cleanup;
> +
> + if (!virFileExists(LOCKSPACE_DIR "/foo"))
> + goto cleanup;
> +
> + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), 0) == 0)
> + goto cleanup;
> +
> + if (virLockSpaceDeleteResource(lockspace, "foo") == 0)
> + goto cleanup;
> +
> + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0)
> + goto cleanup;
> +
> + if (virLockSpaceDeleteResource(lockspace, "foo") < 0)
> + goto cleanup;
> +
> + if (virFileExists(LOCKSPACE_DIR "/foo"))
> + goto cleanup;
> +
> + ret = 0;
> +
> +cleanup:
> + virLockSpaceFree(lockspace);
> + rmdir(LOCKSPACE_DIR);
> + return ret;
> +}
> +
> +
> +static int testLockSpaceResourceLockExclAuto(const void *args ATTRIBUTE_UNUSED)
> +{
> + virLockSpacePtr lockspace;
> + int ret = -1;
> +
> + rmdir(LOCKSPACE_DIR);
> +
> + lockspace = virLockSpaceNew(LOCKSPACE_DIR);
> +
> + if (!virFileIsDir(LOCKSPACE_DIR))
> + goto cleanup;
> +
> + if (virLockSpaceCreateResource(lockspace, "foo") < 0)
> + goto cleanup;
> +
> + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(),
> + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE) < 0)
> + goto cleanup;
> +
> + if (!virFileExists(LOCKSPACE_DIR "/foo"))
> + goto cleanup;
> +
> + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0)
> + goto cleanup;
> +
> + if (virFileExists(LOCKSPACE_DIR "/foo"))
> + goto cleanup;
> +
> + ret = 0;
> +
> +cleanup:
> + virLockSpaceFree(lockspace);
> + rmdir(LOCKSPACE_DIR);
> + return ret;
> +}
> +
> +
> +static int testLockSpaceResourceLockShr(const void *args ATTRIBUTE_UNUSED)
> +{
> + virLockSpacePtr lockspace;
> + int ret = -1;
> +
> + rmdir(LOCKSPACE_DIR);
> +
> + lockspace = virLockSpaceNew(LOCKSPACE_DIR);
> +
> + if (!virFileIsDir(LOCKSPACE_DIR))
> + goto cleanup;
> +
> + if (virLockSpaceCreateResource(lockspace, "foo") < 0)
> + goto cleanup;
> +
> + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(),
> + VIR_LOCK_SPACE_ACQUIRE_SHARED) < 0)
> + goto cleanup;
> +
> + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), 0) == 0)
> + goto cleanup;
> +
> + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(),
> + VIR_LOCK_SPACE_ACQUIRE_SHARED) < 0)
> + goto cleanup;
> +
> + if (virLockSpaceDeleteResource(lockspace, "foo") == 0)
> + goto cleanup;
> +
> + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0)
> + goto cleanup;
> +
> + if (virLockSpaceDeleteResource(lockspace, "foo") == 0)
> + goto cleanup;
> +
> + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0)
> + goto cleanup;
> +
> + if (virLockSpaceDeleteResource(lockspace, "foo") < 0)
> + goto cleanup;
> +
> + if (virFileExists(LOCKSPACE_DIR "/foo"))
> + goto cleanup;
> +
> + ret = 0;
> +
> +cleanup:
> + virLockSpaceFree(lockspace);
> + rmdir(LOCKSPACE_DIR);
> + return ret;
> +}
> +
> +
> +static int testLockSpaceResourceLockShrAuto(const void *args ATTRIBUTE_UNUSED)
> +{
> + virLockSpacePtr lockspace;
> + int ret = -1;
> +
> + rmdir(LOCKSPACE_DIR);
> +
> + lockspace = virLockSpaceNew(LOCKSPACE_DIR);
> +
> + if (!virFileIsDir(LOCKSPACE_DIR))
> + goto cleanup;
> +
> + if (virLockSpaceCreateResource(lockspace, "foo") < 0)
> + goto cleanup;
> +
> + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(),
> + VIR_LOCK_SPACE_ACQUIRE_SHARED |
> + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE) < 0)
> + goto cleanup;
> +
> + if (!virFileExists(LOCKSPACE_DIR "/foo"))
> + goto cleanup;
> +
> + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(),
> + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE) == 0)
> + goto cleanup;
> +
> + if (!virFileExists(LOCKSPACE_DIR "/foo"))
> + goto cleanup;
> +
> + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(),
> + VIR_LOCK_SPACE_ACQUIRE_SHARED |
> + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE) < 0)
> + goto cleanup;
> +
> + if (!virFileExists(LOCKSPACE_DIR "/foo"))
> + goto cleanup;
> +
> + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0)
> + goto cleanup;
> +
> + if (!virFileExists(LOCKSPACE_DIR "/foo"))
> + goto cleanup;
> +
> + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0)
> + goto cleanup;
> +
> + if (virFileExists(LOCKSPACE_DIR "/foo"))
> + goto cleanup;
> +
> + ret = 0;
> +
> +cleanup:
> + virLockSpaceFree(lockspace);
> + rmdir(LOCKSPACE_DIR);
> + return ret;
> +}
> +
> +
> +static int testLockSpaceResourceLockPath(const void *args ATTRIBUTE_UNUSED)
> +{
> + virLockSpacePtr lockspace;
> + int ret = -1;
> +
> + rmdir(LOCKSPACE_DIR);
> +
> + lockspace = virLockSpaceNew(NULL);
> +
> + mkdir(LOCKSPACE_DIR, 0700);
> +
> + if (virLockSpaceCreateResource(lockspace, LOCKSPACE_DIR "/foo") < 0)
> + goto cleanup;
> +
> + if (virLockSpaceAcquireResource(lockspace, LOCKSPACE_DIR "/foo", geteuid(), 0) < 0)
> + goto cleanup;
> +
> + if (!virFileExists(LOCKSPACE_DIR "/foo"))
> + goto cleanup;
> +
> + if (virLockSpaceAcquireResource(lockspace, LOCKSPACE_DIR "/foo", geteuid(), 0) == 0)
> + goto cleanup;
> +
> + if (virLockSpaceDeleteResource(lockspace, LOCKSPACE_DIR "/foo") == 0)
> + goto cleanup;
> +
> + if (virLockSpaceReleaseResource(lockspace, LOCKSPACE_DIR "/foo", geteuid()) < 0)
> + goto cleanup;
> +
> + if (virLockSpaceDeleteResource(lockspace, LOCKSPACE_DIR "/foo") < 0)
> + goto cleanup;
> +
> + if (virFileExists(LOCKSPACE_DIR "/foo"))
> + goto cleanup;
> +
> + ret = 0;
> +
> +cleanup:
> + virLockSpaceFree(lockspace);
> + rmdir(LOCKSPACE_DIR);
> + return ret;
> +}
> +
> +
> +
> +static int
> +mymain(void)
> +{
> + int ret = 0;
> +
> + signal(SIGPIPE, SIG_IGN);
> +
> + if (virtTestRun("Lockspace creation", 1, testLockSpaceCreate, NULL) < 0)
> + ret = -1;
> +
> + if (virtTestRun("Lockspace res lifecycle", 1, testLockSpaceResourceLifecycle, NULL) < 0)
> + ret = -1;
> +
> + if (virtTestRun("Lockspace res lock excl", 1, testLockSpaceResourceLockExcl, NULL) < 0)
> + ret = -1;
> +
> + if (virtTestRun("Lockspace res lock shr", 1, testLockSpaceResourceLockShr, NULL) < 0)
> + ret = -1;
> +
> + if (virtTestRun("Lockspace res lock excl auto", 1, testLockSpaceResourceLockExclAuto, NULL) < 0)
> + ret = -1;
> +
> + if (virtTestRun("Lockspace res lock shr auto", 1, testLockSpaceResourceLockShrAuto, NULL) < 0)
> + ret = -1;
> +
> + if (virtTestRun("Lockspace res full path", 1, testLockSpaceResourceLockPath, NULL) < 0)
> + ret = -1;
> +
> + return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
> +}
> +
> +VIRT_TEST_MAIN(mymain)
>
More information about the libvir-list
mailing list