Imported Upstream version 3.1.0
[debian/amanda] / common-src / amflock.c
index c10599683681a8068ff14bfab5a4f1bcd5fa4c3e..177266dd07c52bd4f5e03bac41fa23689b59d00e 100644 (file)
  * file locking routines, put here to hide the system dependant stuff
  * from the rest of the code
  */
+
+#include "amanda.h"
+
+/*
+ * New Implementation
+ */
+
+static GStaticMutex lock_lock = G_STATIC_MUTEX_INIT;
+static GHashTable *locally_locked_files = NULL;
+
+file_lock *
+file_lock_new(
+    const char *filename)
+{
+    file_lock *lock = g_new0(file_lock, 1);
+    lock->filename = g_strdup(filename);
+    lock->fd = -1;
+
+    return lock;
+}
+
+void
+file_lock_free(
+    file_lock *lock)
+{
+    g_static_mutex_lock(&lock_lock);
+    if (locally_locked_files) {
+       g_hash_table_remove(locally_locked_files,
+                           lock->filename);
+    }
+
+    if (lock->data)
+       g_free(lock->data);
+    if (lock->filename)
+       g_free(lock->filename);
+
+    if (lock->fd != -1)
+       close(lock->fd);
+
+    g_static_mutex_unlock(&lock_lock);
+}
+
+int
+file_lock_lock(
+    file_lock *lock)
+{
+    int rv = -2;
+    int fd = -1;
+    int saved_errno;
+    struct flock lock_buf;
+    struct stat stat_buf;
+
+    g_assert(!lock->locked);
+
+    /* protect from overlapping lock operations within a process */
+    g_static_mutex_lock(&lock_lock);
+    if (!locally_locked_files) {
+       locally_locked_files = g_hash_table_new(g_str_hash, g_str_equal);
+    }
+
+    /* if this filename is in the hash table, then some other thread in this
+     * process has locked it */
+    if (g_hash_table_lookup(locally_locked_files, lock->filename)) {
+       rv = 1;
+       goto done;
+    }
+
+    /* The locks are advisory, so an error here never means the lock is already
+     * taken. */
+    lock->fd = fd = open(lock->filename, O_CREAT|O_RDWR, 0666);
+    if (fd < 0) {
+       rv = -1;
+       goto done;
+    }
+
+    /* now try locking it */
+    lock_buf.l_type = F_WRLCK;
+    lock_buf.l_start = 0;
+    lock_buf.l_whence = SEEK_SET;
+    lock_buf.l_len = 0; /* to EOF */
+    if (fcntl(fd, F_SETLK, &lock_buf) < 0) {
+       if (errno == EACCES || errno == EAGAIN)
+           rv = 1;
+       else
+           rv = -1;
+       goto done;
+    }
+
+    /* and read the file in its entirety */
+    if (fstat(fd, &stat_buf) < 0) {
+       rv = -1;
+       goto done;
+    }
+
+    if (!(stat_buf.st_mode & S_IFREG)) {
+       rv = -1;
+       errno = EINVAL;
+       goto done;
+    }
+
+    if (stat_buf.st_size) {
+       lock->data = g_malloc(stat_buf.st_size);
+       lock->len = stat_buf.st_size;
+       if (full_read(fd, lock->data, lock->len) < lock->len) {
+           rv = -1;
+           goto done;
+       }
+    }
+
+    fd = -1; /* we'll keep the file now */
+    lock->locked = TRUE;
+
+    /* the lock is acquired; record this in the hash table */
+    g_hash_table_insert(locally_locked_files, lock->filename, lock->filename);
+
+    rv = 0;
+
+done:
+    saved_errno = errno;
+    g_static_mutex_unlock(&lock_lock);
+    if (fd >= 0) /* close and unlock if an error occurred */
+       close(fd);
+    errno = saved_errno;
+    return rv;
+}
+
+int
+file_lock_write(
+    file_lock *lock,
+    const char *data,
+    size_t len)
+{
+    int fd = lock->fd;
+
+    g_assert(lock->locked);
+
+    /* seek to position 0, rewrite, and truncate */
+    if (lseek(fd, 0, SEEK_SET) < 0)
+       return -1;
+
+    /* from here on out, any errors have corrupted the datafile.. */
+    if (full_write(fd, data, len) < len)
+       return -1;
+
+    if (lock->len > len) {
+       if (ftruncate(fd, len) < 0)
+           return -1;
+    }
+
+    if (lock->data)
+       g_free(lock->data);
+    lock->data = g_strdup(data);
+    lock->len = len;
+
+    return 0;
+}
+
+int
+file_lock_unlock(
+    file_lock *lock)
+{
+    g_assert(lock->locked);
+
+    g_static_mutex_lock(&lock_lock);
+
+    /* relase the filesystem-level lock */
+    close(lock->fd);
+
+    /* and the hash table entry */
+    g_hash_table_remove(locally_locked_files, lock->filename);
+
+    g_static_mutex_unlock(&lock_lock);
+
+    if (lock->data)
+       g_free(lock->data);
+    lock->data = NULL;
+    lock->len = 0;
+    lock->fd = -1;
+    lock->locked = FALSE;
+
+    return 0;
+}
+
+/*
+ * Old Implementation
+ */
+
 /*
 **
 ** Notes:
 **     <none>          - No locking available.  User beware!
 */
 
-/* FIXME: This code has several limitations to be fixed:
- * - It should be possible to select a locking mode (or detect the
- *   best mode for a particular filesystem) at runtime.
- * - There should be a locking mode that works with NFS filesystems.
- * - Semantics should be clear when different parts of a single 
- *   process (possibly in the same/different threads) both try to lock 
- *   the same file (but with different file descriptors).
- * - It should be possible to promote a read-only lock to an 
- *   exclusive lock.
- * - Arbitrary strings should be useable as resource names. */
-
-#include "amanda.h"
 /* Interface to the implementations in common-src/amflock-*.c */
 
 #ifdef WANT_AMFLOCK_POSIX