diff --git a/.gitignore b/.gitignore
index 174209352b2c4106b68b81ebc8404948c040401b..c185cbcc446d6adbb996f9e3cce0c2c572346dfe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -165,6 +165,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 51dd435d74e41aa070255311c10a30f9273983f9..a87768349bc5c00cebd3550b73de7d97322238a7 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 815e99270562ad5c6c092cd374d43c0d595863cc..23f675d5e82e8c68f3ebb51164a6ac287f5bb616 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -150,6 +150,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 4f19bcfe0763c86de79b618f3f55a97a4cd97311..9d3194d909c3cb98583b7bc5ef9724aff327a851 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -97,6 +97,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 0bd5a1216a63d4bb7485a5d913f37c183d091225..b5a5948ce2ac10ee78e0be7c90582ad99c4d5e8d 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1355,6 +1355,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 0000000000000000000000000000000000000000..e525331d7441700ae9801ddbd0896cf251ef93a3
--- /dev/null
+++ b/src/util/virlockspace.c
@@ -0,0 +1,560 @@
+/*
+ * 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
+ * .
+ *
+ */
+
+#include
+
+#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
+#include
+#include
+
+#define VIR_FROM_THIS VIR_FROM_LOCKSPACE
+
+#define VIR_LOCKSPACE_TABLE_SIZE 10
+
+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, 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;
+ }
+ }
+ 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(VIR_LOCKSPACE_TABLE_SIZE,
+ virLockSpaceResourceDataFree)))
+ 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 0000000000000000000000000000000000000000..bd8f91c0954d261e857b8968ea6f476332ce2833
--- /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
+ * .
+ *
+ */
+
+#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 ed1ff87fed6867e1ae489c965f839fb7a7950123..9572116ca72be063b987b9e1d3429536462e12f0 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",
)
@@ -1205,6 +1206,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 8dbad97d085c672b838d2431a83a1c0060718137..18f5b5163ed5e9f0a0eced940181261e707e2c63 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -93,7 +93,9 @@ test_programs = virshtest sockettest \
utiltest virnettlscontexttest shunloadtest \
virtimetest viruritest virkeyfiletest \
virauthconfigtest \
- virbitmaptest
+ virbitmaptest \
+ virlockspacetest \
+ $(NULL)
if WITH_SECDRIVER_SELINUX
test_programs += securityselinuxtest
@@ -537,6 +539,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 0000000000000000000000000000000000000000..ee58f9512ff1f481c27a813078ae805bbf674e8a
--- /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
+ * .
+ *
+ * Author: Daniel P. Berrange
+ */
+
+#include
+
+#include
+#include
+#include
+
+#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)