]> xenbits.xensource.com Git - libvirt.git/commitdiff
Introduce an internal API for handling file based lockspaces
authorDaniel P. Berrange <berrange@redhat.com>
Thu, 2 Aug 2012 16:02:40 +0000 (17:02 +0100)
committerDaniel P. Berrange <berrange@redhat.com>
Tue, 16 Oct 2012 14:45:55 +0000 (15:45 +0100)
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@redhat.com>
.gitignore
include/libvirt/virterror.h
po/POTFILES.in
src/Makefile.am
src/libvirt_private.syms
src/util/virlockspace.c [new file with mode: 0644]
src/util/virlockspace.h [new file with mode: 0644]
src/util/virterror.c
tests/Makefile.am
tests/virlockspacetest.c [new file with mode: 0644]

index 174209352b2c4106b68b81ebc8404948c040401b..c185cbcc446d6adbb996f9e3cce0c2c572346dfe 100644 (file)
 /tests/virdrivermoduletest
 /tests/virhashtest
 /tests/virkeyfiletest
+/tests/virlockspacetest
 /tests/virnet*test
 /tests/virshtest
 /tests/virtimetest
index 51dd435d74e41aa070255311c10a30f9273983f9..a87768349bc5c00cebd3550b73de7d97322238a7 100644 (file)
@@ -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;
 
 /**
index 815e99270562ad5c6c092cd374d43c0d595863cc..23f675d5e82e8c68f3ebb51164a6ac287f5bb616 100644 (file)
@@ -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
index 4f19bcfe0763c86de79b618f3f55a97a4cd97311..9d3194d909c3cb98583b7bc5ef9724aff327a851 100644 (file)
@@ -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 \
index 0bd5a1216a63d4bb7485a5d913f37c183d091225..b5a5948ce2ac10ee78e0be7c90582ad99c4d5e8d 100644 (file)
@@ -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 (file)
index 0000000..e525331
--- /dev/null
@@ -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
+ * <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
+
+#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 (file)
index 0000000..bd8f91c
--- /dev/null
@@ -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__ */
index ed1ff87fed6867e1ae489c965f839fb7a7950123..9572116ca72be063b987b9e1d3429536462e12f0 100644 (file)
@@ -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;
 }
index 8dbad97d085c672b838d2431a83a1c0060718137..18f5b5163ed5e9f0a0eced940181261e707e2c63 100644 (file)
@@ -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 (file)
index 0000000..ee58f95
--- /dev/null
@@ -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@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)