ulockmgr
diff --git a/ChangeLog b/ChangeLog
index 744ab42..ad21e97 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2006-09-10  Miklos Szeredi <miklos@szeredi.hu>
+
+	* Released 2.6.0-rc1
+
+2006-09-10  Miklos Szeredi <miklos@szeredi.hu>
+
+	* kernel: Fix unlock on close for kernels < 2.6.18
+
+	* Add ulockmgr library & server.  This can be used for handling
+	file locking requests either directly from libfuse or over a
+	network, etc.  This first version is not optimized and the number
+	of file descriptors it uses may get out of hand
+
 2006-09-07  Miklos Szeredi <miklos@szeredi.hu>
 
 	* lib: Add interrupt support to high level library, which may be
diff --git a/configure.in b/configure.in
index 063f68f..f21326e 100644
--- a/configure.in
+++ b/configure.in
@@ -1,4 +1,4 @@
-AC_INIT(fuse, 2.6.0-pre3)
+AC_INIT(fuse, 2.6.0-rc1)
 AC_CANONICAL_TARGET
 AM_INIT_AUTOMAKE
 AM_CONFIG_HEADER(include/config.h)
diff --git a/example/Makefile.am b/example/Makefile.am
index 1f31cfc..9fe35e8 100644
--- a/example/Makefile.am
+++ b/example/Makefile.am
@@ -3,9 +3,5 @@
 AM_CPPFLAGS = -I$(top_srcdir)/include
 noinst_PROGRAMS = fusexmp fusexmp_fh null hello hello_ll
 
-fusexmp_SOURCES = fusexmp.c
-fusexmp_fh_SOURCES = fusexmp_fh.c
-null_SOURCES = null.c
-hello_SOURCES = hello.c
-
 LDADD = ../lib/libfuse.la -lpthread
+fusexmp_fh_LDADD = ../lib/libfuse.la ../lib/libulockmgr.la -lpthread
diff --git a/example/fusexmp_fh.c b/example/fusexmp_fh.c
index 5ede6db..047ad09 100644
--- a/example/fusexmp_fh.c
+++ b/example/fusexmp_fh.c
@@ -11,6 +11,7 @@
 #define _GNU_SOURCE
 
 #include <fuse.h>
+#include <ulockmgr.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
@@ -400,6 +401,14 @@
 }
 #endif /* HAVE_SETXATTR */
 
+static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
+                    struct flock *lock, uint64_t owner)
+{
+    (void) path;
+
+    return ulockmgr_op(fi->fh, cmd, lock, &owner, sizeof(owner));
+}
+
 static struct fuse_operations xmp_oper = {
     .getattr	= xmp_getattr,
     .fgetattr	= xmp_fgetattr,
@@ -434,6 +443,7 @@
     .listxattr	= xmp_listxattr,
     .removexattr= xmp_removexattr,
 #endif
+    .lock	= xmp_lock,
 };
 
 int main(int argc, char *argv[])
diff --git a/include/Makefile.am b/include/Makefile.am
index c786a0d..be2c592 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -11,6 +11,6 @@
 	fuse_lowlevel_compat.h	\
 	fuse_opt.h
 
-include_HEADERS = old/fuse.h
+include_HEADERS = old/fuse.h ulockmgr.h
 
 noinst_HEADERS = fuse_kernel.h
diff --git a/include/ulockmgr.h b/include/ulockmgr.h
new file mode 100644
index 0000000..4cd7b1c
--- /dev/null
+++ b/include/ulockmgr.h
@@ -0,0 +1,23 @@
+/*
+    libulockmgr: Userspace Lock Manager Library
+    Copyright (C) 2006  Miklos Szeredi <miklos@szeredi.hu>
+
+    This program can be distributed under the terms of the GNU LGPL.
+    See the file COPYING.LIB.
+*/
+
+#include <stdint.h>
+#include <fcntl.h>
+
+/**
+ * Perform POSIX locking operation
+ *
+ * @param fd the file descriptor
+ * @param cmd the locking command (F_GETFL, F_SETLK or F_SETLKW)
+ * @param lock the lock parameters
+ * @param owner the lock owner ID cookie
+ * @param owner_len length of the lock owner ID cookie
+ * @return 0 on success -errno on error
+ */
+int ulockmgr_op(int fd, int cmd, struct flock *lock, const void *owner,
+                size_t owner_len);
diff --git a/kernel/configure.ac b/kernel/configure.ac
index c3c1f3f..910e125 100644
--- a/kernel/configure.ac
+++ b/kernel/configure.ac
@@ -1,4 +1,4 @@
-AC_INIT(fuse-kernel, 2.6.0-pre3)
+AC_INIT(fuse-kernel, 2.6.0-rc1)
 AC_CONFIG_HEADERS([config.h])
 
 AC_PROG_INSTALL
diff --git a/kernel/file.c b/kernel/file.c
index 61eba56..e3a42c0 100644
--- a/kernel/file.c
+++ b/kernel/file.c
@@ -867,9 +867,32 @@
 	/* Unlock on close is handled by the flush method */
 	if (fl->fl_flags & FL_CLOSE)
 		return 0;
-#endif
 
 	req = fuse_get_req(fc);
+#else
+	/* If it's (possibly) unlock on close, don't fail the allocation */
+	if (fl->fl_type == F_UNLCK && fl->fl_start == 0 &&
+	    fl->fl_end == OFFSET_MAX)
+		req = fuse_get_req_nofail(fc, file);
+	else {
+		/* Hack: add dummy lock, otherwise unlock on close is
+		   optimized away */
+		struct file_lock **flp;
+		for (flp = &inode->i_flock;
+		     *flp && !((*flp)->fl_flags & FL_POSIX);
+		     flp = &(*flp)->fl_next);
+		if (!*flp) {
+			struct file_lock *dummy =
+				kmalloc(sizeof(struct file_lock), GFP_KERNEL);
+			if (!dummy)
+				return -ENOLCK;
+			locks_init_lock(dummy);
+			dummy->fl_flags |= FL_POSIX;
+			*flp = dummy;
+		}
+		req = fuse_get_req(fc);
+	}
+#endif
 	if (IS_ERR(req))
 		return PTR_ERR(req);
 
diff --git a/kernel/inode.c b/kernel/inode.c
index 0145045..475513b 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -83,6 +83,13 @@
 	struct fuse_inode *fi = get_fuse_inode(inode);
 	if (fi->forget_req)
 		fuse_request_free(fi->forget_req);
+#ifndef KERNEL_2_6_18_PLUS
+	if (inode->i_flock) {
+		WARN_ON(inode->i_flock->fl_next);
+		kfree(inode->i_flock);
+		inode->i_flock = NULL;
+	}
+#endif
 	kmem_cache_free(fuse_inode_cachep, inode);
 }
 
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 5206493..87e323a 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,7 +1,7 @@
 ## Process this file with automake to produce Makefile.in
 
 AM_CPPFLAGS = -I$(top_srcdir)/include -DFUSERMOUNT_DIR=\"$(bindir)\"
-lib_LTLIBRARIES = libfuse.la
+lib_LTLIBRARIES = libfuse.la libulockmgr.la
 
 if BSD
 mount_source = mount_bsd.c
@@ -27,4 +27,7 @@
 libfuse_la_LDFLAGS = $(libfuse_libs) -version-number 2:6:0 \
 	-Wl,--version-script,$(srcdir)/fuse_versionscript
 
+libulockmgr_la_SOURCES = ulockmgr.c
+libulockmgr_la_LDFLAGS = -version-number 1:0:0
+
 EXTRA_DIST = fuse_versionscript
diff --git a/lib/fuse.c b/lib/fuse.c
index a2f6bb1..92f21db 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -34,6 +34,7 @@
 #define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1
 
 #define FUSE_UNKNOWN_INO 0xffffffff
+#define OFFSET_MAX 0x7fffffffffffffffLL
 
 struct fuse_config {
     unsigned int uid;
@@ -76,6 +77,15 @@
     int intr_installed;
 };
 
+struct lock {
+    int type;
+    off_t start;
+    off_t end;
+    pid_t pid;
+    uint64_t owner;
+    struct lock *next;
+};
+
 struct node {
     struct node *name_next;
     struct node *id_next;
@@ -91,6 +101,7 @@
     struct timespec mtime;
     off_t size;
     int cache_valid;
+    struct lock *locks;
 };
 
 struct fuse_dirhandle {
@@ -1604,43 +1615,6 @@
         reply_err(req, res);
 }
 
-static void fuse_flush(fuse_req_t req, fuse_ino_t ino,
-                       struct fuse_file_info *fi, uint64_t owner)
-{
-    struct fuse *f = req_fuse_prepare(req);
-    char *path;
-    int err;
-
-    err = -ENOENT;
-    pthread_rwlock_rdlock(&f->tree_lock);
-    path = get_path(f, ino);
-    if (path != NULL) {
-        if (f->conf.debug) {
-            printf("FLUSH[%llu]\n", (unsigned long long) fi->fh);
-            fflush(stdout);
-        }
-        err = -ENOSYS;
-        if (f->op.flush) {
-            struct fuse_intr_data d;
-            fuse_prepare_interrupt(f, req, &d);
-            err = f->op.flush(path, fi);
-            fuse_finish_interrupt(f, req, &d);
-        }
-        free(path);
-    }
-    if (f->op.lock) {
-        struct flock lock;
-        memset(&lock, 0, sizeof(lock));
-        lock.l_type = F_UNLCK;
-        lock.l_whence = SEEK_SET;
-        fuse_do_lock(f, req, path, fi, F_SETLK, &lock, owner);
-        if (err == -ENOSYS)
-            err = 0;
-    }
-    pthread_rwlock_unlock(&f->tree_lock);
-    reply_err(req, err);
-}
-
 static void fuse_release(fuse_req_t req, fuse_ino_t ino,
                          struct fuse_file_info *fi)
 {
@@ -2162,6 +2136,167 @@
     reply_err(req, err);
 }
 
+static struct lock *locks_conflict(struct node *node, const struct lock *lock)
+{
+    struct lock *l;
+
+    for (l = node->locks; l; l = l->next)
+        if (l->owner != lock->owner &&
+            lock->start <= l->end && l->start <= lock->end &&
+            (l->type == F_WRLCK || lock->type == F_WRLCK))
+            break;
+
+    return l;
+}
+
+static void delete_lock(struct lock **lockp)
+{
+    struct lock *l = *lockp;
+    *lockp = l->next;
+    free(l);
+}
+
+static void insert_lock(struct lock **pos, struct lock *lock)
+{
+    lock->next = *pos;
+    *pos = lock;
+}
+
+static int locks_insert(struct node *node, struct lock *lock)
+{
+    struct lock **lp;
+    struct lock *newl1 = NULL;
+    struct lock *newl2 = NULL;
+
+    if (lock->type != F_UNLCK || lock->start != 0 || lock->end != OFFSET_MAX) {
+        newl1 = malloc(sizeof(struct lock));
+        newl2 = malloc(sizeof(struct lock));
+
+        if (!newl1 || !newl2) {
+            free(newl1);
+            free(newl2);
+            return -ENOLCK;
+        }
+    }
+
+    for (lp = &node->locks; *lp;) {
+        struct lock *l = *lp;
+        if (l->owner != lock->owner)
+            goto skip;
+
+        if (lock->type == l->type) {
+            if (l->end < lock->start - 1)
+                goto skip;
+            if (lock->end < l->start - 1)
+                break;
+            if (l->start <= lock->start && lock->end <= l->end)
+                goto out;
+            if (l->start < lock->start)
+                lock->start = l->start;
+            if (lock->end < l->end)
+                lock->end = l->end;
+            goto delete;
+        } else {
+            if (l->end < lock->start)
+                goto skip;
+            if (lock->end < l->start)
+                break;
+            if (lock->start <= l->start && l->end <= lock->end)
+                goto delete;
+            if (l->end <= lock->end) {
+                l->end = lock->start - 1;
+                goto skip;
+            }
+            if (lock->start <= l->start) {
+                l->start = lock->end + 1;
+                break;
+            }
+            *newl2 = *l;
+            newl2->start = lock->end + 1;
+            l->end = lock->start - 1;
+            insert_lock(&l->next, newl2);
+            newl2 = NULL;
+        }
+    skip:
+        lp = &l->next;
+        continue;
+
+    delete:
+        delete_lock(lp);
+    }
+    if (lock->type != F_UNLCK) {
+        *newl1 = *lock;
+        insert_lock(lp, newl1);
+        newl1 = NULL;
+    }
+out:
+    free(newl1);
+    free(newl2);
+    return 0;
+}
+
+static void flock_to_lock(struct flock *flock, struct lock *lock)
+{
+    memset(lock, 0, sizeof(struct lock));
+    lock->type = flock->l_type;
+    lock->start = flock->l_start;
+    lock->end = flock->l_len ? flock->l_start + flock->l_len - 1 : OFFSET_MAX;
+    lock->pid = flock->l_pid;
+}
+
+static void lock_to_flock(struct lock *lock, struct flock *flock)
+{
+    flock->l_type = lock->type;
+    flock->l_start = lock->start;
+    flock->l_len = (lock->end == OFFSET_MAX) ? 0 : lock->end - lock->start + 1;
+    flock->l_pid = lock->pid;
+}
+
+static void fuse_flush(fuse_req_t req, fuse_ino_t ino,
+                       struct fuse_file_info *fi, uint64_t owner)
+{
+    struct fuse *f = req_fuse_prepare(req);
+    char *path;
+    int err;
+
+    err = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path != NULL) {
+        if (f->conf.debug) {
+            printf("FLUSH[%llu]\n", (unsigned long long) fi->fh);
+            fflush(stdout);
+        }
+        err = -ENOSYS;
+        if (f->op.flush) {
+            struct fuse_intr_data d;
+            fuse_prepare_interrupt(f, req, &d);
+            err = f->op.flush(path, fi);
+            fuse_finish_interrupt(f, req, &d);
+        }
+        free(path);
+    }
+    if (f->op.lock) {
+        struct flock lock;
+        struct lock l;
+        memset(&lock, 0, sizeof(lock));
+        lock.l_type = F_UNLCK;
+        lock.l_whence = SEEK_SET;
+        fuse_do_lock(f, req, path, fi, F_SETLK, &lock, owner);
+        flock_to_lock(&lock, &l);
+        l.owner = owner;
+        pthread_mutex_lock(&f->lock);
+        locks_insert(get_node(f, ino), &l);
+        pthread_mutex_unlock(&f->lock);
+
+        /* if op.lock() is defined FLUSH is needed regardless of op.flush() */
+        if (err == -ENOSYS)
+            err = 0;
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    reply_err(req, err);
+}
+
 static int fuse_lock_common(fuse_req_t req, fuse_ino_t ino,
                             struct fuse_file_info *fi, struct flock *lock,
                             uint64_t owner, int cmd)
@@ -2174,7 +2309,7 @@
     pthread_rwlock_rdlock(&f->tree_lock);
     path = get_path(f, ino);
     if (path != NULL) {
-        fuse_do_lock(f, req, path, fi, cmd, lock, owner);
+        err = fuse_do_lock(f, req, path, fi, cmd, lock, owner);
         free(path);
     }
     pthread_rwlock_unlock(&f->tree_lock);
@@ -2185,7 +2320,23 @@
                        struct fuse_file_info *fi, struct flock *lock,
                        uint64_t owner)
 {
-    int err = fuse_lock_common(req, ino, fi, lock, owner, F_GETLK);
+    int err;
+    struct lock l;
+    struct lock *conflict;
+    struct fuse *f = req_fuse(req);
+
+    flock_to_lock(lock, &l);
+    l.owner = owner;
+    pthread_mutex_lock(&f->lock);
+    conflict = locks_conflict(get_node(f, ino), &l);
+    if (conflict)
+        lock_to_flock(conflict, lock);
+    pthread_mutex_unlock(&f->lock);
+    if (!conflict)
+        err = fuse_lock_common(req, ino, fi, lock, owner, F_GETLK);
+    else
+        err = 0;
+
     if (!err)
         fuse_reply_lock(req, lock);
     else
@@ -2196,8 +2347,18 @@
                        struct fuse_file_info *fi, struct flock *lock,
                        uint64_t owner, int sleep)
 {
-    reply_err(req, fuse_lock_common(req, ino, fi, lock, owner,
-                                    sleep ? F_SETLKW : F_SETLK));
+    int err = fuse_lock_common(req, ino, fi, lock, owner,
+                               sleep ? F_SETLKW : F_SETLK);
+    if (!err) {
+        struct fuse *f = req_fuse(req);
+        struct lock l;
+        flock_to_lock(lock, &l);
+        l.owner = owner;
+        pthread_mutex_lock(&f->lock);
+        locks_insert(get_node(f, ino), &l);
+        pthread_mutex_unlock(&f->lock);
+    }
+    reply_err(req, err);
 }
 
 static struct fuse_lowlevel_ops fuse_path_ops = {
@@ -2418,8 +2579,7 @@
 
         memset(&sa, 0, sizeof(struct sigaction));
         sa.sa_handler = fuse_intr_sighandler;
-        sigemptyset(&(sa.sa_mask));
-        sa.sa_flags = 0;
+        sigemptyset(&sa.sa_mask);
 
         if (sigaction(signum, &sa, NULL) == -1) {
             perror("fuse: cannot set interrupt signal handler");
diff --git a/lib/ulockmgr.c b/lib/ulockmgr.c
new file mode 100644
index 0000000..795fc41
--- /dev/null
+++ b/lib/ulockmgr.c
@@ -0,0 +1,402 @@
+/*
+    libulockmgr: Userspace Lock Manager Library
+    Copyright (C) 2006  Miklos Szeredi <miklos@szeredi.hu>
+
+    This program can be distributed under the terms of the GNU LGPL.
+    See the file COPYING.LIB
+*/
+
+/* #define DEBUG 1 */
+
+#include "ulockmgr.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+struct message {
+    int intr;
+    pthread_t thr;
+    int cmd;
+    int fd;
+    struct flock lock;
+    int error;
+};
+
+struct fd_store {
+    struct fd_store *next;
+    int fd;
+    int finished;
+};
+
+struct owner {
+    struct owner *next;
+    struct owner *prev;
+    struct fd_store *fds;
+    void *id;
+    size_t id_len;
+    int cfd;
+};
+
+static pthread_mutex_t ulockmgr_lock;
+static int ulockmgr_cfd = -1;
+static struct owner owner_list = { .next = &owner_list, .prev = &owner_list };
+
+#define MAX_SEND_FDS 2
+
+static void list_del_owner(struct owner *owner)
+{
+    struct owner *prev = owner->prev;
+    struct owner *next = owner->next;
+    prev->next = next;
+    next->prev = prev;
+}
+
+static void list_add_owner(struct owner *owner, struct owner *next)
+{
+    struct owner *prev = next->prev;
+    owner->next = next;
+    owner->prev = prev;
+    prev->next = owner;
+    next->prev = owner;
+}
+
+static int ulockmgr_send_message(int sock, void *buf, size_t buflen,
+                                 int *fdp, int numfds)
+{
+    struct msghdr msg;
+    struct cmsghdr *p_cmsg;
+    struct iovec vec;
+    char cmsgbuf[CMSG_SPACE(sizeof(int) * MAX_SEND_FDS)];
+    int res;
+
+    assert(numfds <= MAX_SEND_FDS);
+    msg.msg_control = cmsgbuf;
+    msg.msg_controllen = sizeof(cmsgbuf);
+    p_cmsg = CMSG_FIRSTHDR(&msg);
+    p_cmsg->cmsg_level = SOL_SOCKET;
+    p_cmsg->cmsg_type = SCM_RIGHTS;
+    p_cmsg->cmsg_len = CMSG_LEN(sizeof(int) * numfds);
+    memcpy(CMSG_DATA(p_cmsg), fdp, sizeof(int) * numfds);
+    msg.msg_controllen = p_cmsg->cmsg_len;
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+    msg.msg_iov = &vec;
+    msg.msg_iovlen = 1;
+    msg.msg_flags = 0;
+    vec.iov_base = buf;
+    vec.iov_len = buflen;
+    res = sendmsg(sock, &msg, MSG_NOSIGNAL);
+    if (res == -1) {
+        perror("libulockmgr: sendmsg");
+        return -1;
+    }
+    if ((size_t) res != buflen) {
+        fprintf(stderr, "libulockmgr: sendmsg short\n");
+        return -1;
+    }
+    return 0;
+}
+
+static int ulockmgr_start_daemon(void)
+{
+    int sv[2];
+    int res;
+    char tmp[64];
+
+    res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+    if (res == -1) {
+        perror("libulockmgr: socketpair");
+        return -1;
+    }
+    snprintf(tmp, sizeof(tmp), "exec ulockmgr_server %i", sv[0]);
+    res = system(tmp);
+    close(sv[0]);
+    if (res == -1 || !WIFEXITED(res) || WEXITSTATUS(res) != 0) {
+        close(sv[1]);
+        return -1;
+    }
+    ulockmgr_cfd = sv[1];
+    return 0;
+}
+
+static struct owner *ulockmgr_new_owner(const void *id, size_t id_len)
+{
+    int sv[2];
+    int res;
+    char c = 'm';
+    struct owner *o;
+
+    if (ulockmgr_cfd == -1 && ulockmgr_start_daemon() == -1)
+        return NULL;
+
+    o = calloc(1, sizeof(struct owner) + id_len);
+    if (!o) {
+        fprintf(stderr, "libulockmgr: failed to allocate memory\n");
+        return NULL;
+    }
+    o->id = o + 1;
+    o->id_len = id_len;
+    res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+    if (res == -1) {
+        perror("libulockmgr: socketpair");
+        goto out_free;
+    }
+    res = ulockmgr_send_message(ulockmgr_cfd, &c, sizeof(c), &sv[0], 1);
+    close(sv[0]);
+    if (res == -1) {
+        close(ulockmgr_cfd);
+        ulockmgr_cfd = -1;
+        goto out_close;
+    }
+
+    o->cfd = sv[1];
+    memcpy(o->id, id, id_len);
+    list_add_owner(o, &owner_list);
+
+    return o;
+
+ out_close:
+    close(sv[1]);
+ out_free:
+    free(o);
+    return NULL;
+}
+
+static int ulockmgr_send_request(struct message *msg, const void *id,
+                                 size_t id_len)
+{
+    int sv[2];
+    int cfd;
+    struct owner *o;
+    struct fd_store *f;
+    int fd = msg->fd;
+    int cmd = msg->cmd;
+    int res;
+    int unlockall = (cmd == F_SETLK && msg->lock.l_type == F_UNLCK &&
+                     msg->lock.l_start == 0 && msg->lock.l_len == 0);
+
+    for (o = owner_list.next; o != &owner_list; o = o->next)
+        if (o->id_len == id_len && memcmp(o->id, id, id_len) == 0)
+            break;
+
+    if (o == &owner_list)
+        o = NULL;
+
+    if (!o && cmd != F_GETLK && msg->lock.l_type != F_UNLCK)
+        o = ulockmgr_new_owner(id, id_len);
+
+    if (!o) {
+        if (cmd == F_GETLK) {
+            res = fcntl(msg->fd, F_GETLK, &msg->lock);
+            return (res == -1) ? -errno : 0;
+        } else if (msg->lock.l_type == F_UNLCK)
+            return 0;
+        else
+            return -ENOLCK;
+    }
+
+    f = calloc(1, sizeof(struct fd_store));
+    if (!f) {
+        fprintf(stderr, "libulockmgr: failed to allocate memory\n");
+        return -ENOLCK;
+    }
+
+    res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+    if (res == -1) {
+        perror("libulockmgr: socketpair");
+        free(f);
+        return -ENOLCK;
+    }
+
+    cfd = sv[1];
+    sv[1] = msg->fd;
+    res = ulockmgr_send_message(o->cfd, msg, sizeof(struct message), sv, 2);
+    close(sv[0]);
+    if (res == -1) {
+        free(f);
+        close(cfd);
+        return -EIO;
+    }
+
+    f->fd = msg->fd;
+    f->next = o->fds;
+    o->fds = f;
+
+    res = recv(cfd, msg, sizeof(struct message), MSG_WAITALL);
+    if (res == -1) {
+        perror("libulockmgr: recv");
+        msg->error = EIO;
+    } else if (res != sizeof(struct message)) {
+        fprintf(stderr, "libulockmgr: recv short\n");
+        msg->error = EIO;
+    } else if (cmd == F_SETLKW && msg->error == EAGAIN) {
+        pthread_mutex_unlock(&ulockmgr_lock);
+        while (1) {
+            sigset_t old;
+            sigset_t unblock;
+            int errno_save;
+
+            sigemptyset(&unblock);
+            sigaddset(&unblock, SIGUSR1);
+            pthread_sigmask(SIG_UNBLOCK, &unblock, &old);
+            res = recv(cfd, msg, sizeof(struct message), MSG_WAITALL);
+            errno_save = errno;
+            pthread_sigmask(SIG_SETMASK, &old, NULL);
+            if (res == sizeof(struct message))
+                break;
+            else if (res >= 0) {
+                fprintf(stderr, "libulockmgr: recv short\n");
+                msg->error = EIO;
+                break;
+            } else if (errno_save != EINTR) {
+                errno = errno_save;
+                perror("libulockmgr: recv");
+                msg->error = EIO;
+                break;
+            }
+            msg->intr = 1;
+            res = send(o->cfd, msg, sizeof(struct message), MSG_NOSIGNAL);
+            if (res == -1) {
+                perror("libulockmgr: send");
+                msg->error = EIO;
+                break;
+            }
+            if (res != sizeof(struct message)) {
+                fprintf(stderr, "libulockmgr: send short\n");
+                msg->error = EIO;
+                break;
+            }
+        }
+        pthread_mutex_lock(&ulockmgr_lock);
+
+    }
+
+    f->finished = 1;
+    close(cfd);
+    if (unlockall) {
+        struct fd_store **fp;
+
+        for (fp = &o->fds; *fp;) {
+            f = *fp;
+            if (f->fd == fd && f->finished) {
+                *fp = f->next;
+                free(f);
+            } else
+                fp = &f->next;
+        }
+        if (!o->fds) {
+            list_del_owner(o);
+            close(o->cfd);
+            free(o);
+        }
+        /* Force OK on unlock-all, since it _will_ succeed once the
+           owner is deleted */
+        msg->error = 0;
+    }
+
+    return -msg->error;
+}
+
+#ifdef DEBUG
+static uint32_t owner_hash(const unsigned char *id, size_t id_len)
+{
+    uint32_t h = 0;
+    size_t i;
+    for (i = 0; i < id_len; i++)
+        h = ((h << 8) | (h >> 24)) ^ id[i];
+
+    return h;
+}
+#endif
+
+static int ulockmgr_canonicalize(int fd, struct flock *lock)
+{
+    off_t offset;
+    if (lock->l_whence == SEEK_CUR) {
+        offset = lseek(fd, 0, SEEK_CUR);
+        if (offset == (off_t) -1)
+            return -errno;
+    } else if (lock->l_whence == SEEK_END) {
+        struct stat stbuf;
+        int res = fstat(fd, &stbuf);
+        if (res == -1)
+            return -errno;
+
+        offset = stbuf.st_size;
+    } else
+        offset = 0;
+
+    lock->l_whence = SEEK_SET;
+    lock->l_start += offset;
+
+    if (lock->l_start < 0)
+        return -EINVAL;
+
+    if (lock->l_len < 0) {
+        lock->l_start += lock->l_len;
+        if (lock->l_start < 0)
+            return -EINVAL;
+        lock->l_len = -lock->l_len;
+    }
+    if (lock->l_len && lock->l_start + lock->l_len - 1 < 0)
+        return -EINVAL;
+
+    return 0;
+}
+
+int ulockmgr_op(int fd, int cmd, struct flock *lock, const void *owner,
+                size_t owner_len)
+{
+    int err;
+    struct message msg;
+    sigset_t old;
+    sigset_t block;
+
+    if (cmd != F_GETLK && cmd != F_SETLK && cmd != F_SETLKW)
+        return -EINVAL;
+
+    if (lock->l_whence != SEEK_SET && lock->l_whence != SEEK_CUR &&
+        lock->l_whence != SEEK_END)
+        return -EINVAL;
+
+#ifdef DEBUG
+    fprintf(stderr, "libulockmgr: %i %i %i %lli %lli own: 0x%08x\n",
+            cmd, lock->l_type, lock->l_whence, lock->l_start, lock->l_len,
+            owner_hash(owner, owner_len));
+#endif
+
+    /* Unlock should never block anyway */
+    if (cmd == F_SETLKW && lock->l_type == F_UNLCK)
+        cmd = F_SETLK;
+
+    memset(&msg, 0, sizeof(struct message));
+    msg.cmd = cmd;
+    msg.fd = fd;
+    msg.lock = *lock;
+    err = ulockmgr_canonicalize(fd, &msg.lock);
+    if (err)
+        return err;
+
+    sigemptyset(&block);
+    sigaddset(&block, SIGUSR1);
+    pthread_sigmask(SIG_BLOCK, &block, &old);
+    pthread_mutex_lock(&ulockmgr_lock);
+    err = ulockmgr_send_request(&msg, owner, owner_len);
+    pthread_mutex_unlock(&ulockmgr_lock);
+    pthread_sigmask(SIG_SETMASK, &old, NULL);
+    if (!err && cmd == F_GETLK) {
+        if (msg.lock.l_type == F_UNLCK)
+            lock->l_type = F_UNLCK;
+        else
+            *lock = msg.lock;
+    }
+
+    return err;
+}
diff --git a/util/.cvsignore b/util/.cvsignore
index 87461c8..d7a4699 100644
--- a/util/.cvsignore
+++ b/util/.cvsignore
@@ -3,4 +3,5 @@
 .deps
 .libs
 fusermount
+ulockmgr_server
 fuse_ioslave
diff --git a/util/Makefile.am b/util/Makefile.am
index c07440a..8e8db2a 100644
--- a/util/Makefile.am
+++ b/util/Makefile.am
@@ -1,9 +1,12 @@
 ## Process this file with automake to produce Makefile.in
 
-bin_PROGRAMS = fusermount
+bin_PROGRAMS = fusermount ulockmgr_server
 
 fusermount_SOURCES = fusermount.c
 
+ulockmgr_server_SOURCES = ulockmgr_server.c
+ulockmgr_server_LDFLAGS = -pthread
+
 install-exec-hook:
 	-chown root $(DESTDIR)$(bindir)/fusermount
 	-chmod u+s $(DESTDIR)$(bindir)/fusermount
diff --git a/util/ulockmgr_server.c b/util/ulockmgr_server.c
new file mode 100644
index 0000000..a2d6863
--- /dev/null
+++ b/util/ulockmgr_server.c
@@ -0,0 +1,347 @@
+/*
+    ulockmgr_server: Userspace Lock Manager Server
+    Copyright (C) 2006  Miklos Szeredi <miklos@szeredi.hu>
+
+    This program can be distributed under the terms of the GNU GPL.
+    See the file COPYING.
+*/
+
+/* #define DEBUG 1 */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+struct message {
+    int intr;
+    pthread_t thr;
+    int cmd;
+    int fd;
+    struct flock lock;
+    int error;
+};
+
+struct fd_store {
+    struct fd_store *next;
+    int fd;
+    int origfd;
+    int finished;
+};
+
+struct owner {
+    struct fd_store *fds;
+    pthread_mutex_t lock;
+};
+
+struct req_data {
+    struct owner *o;
+    int cfd;
+    struct fd_store *f;
+    struct message msg;
+};
+
+#define MAX_SEND_FDS 2
+
+static int receive_message(int sock, void *buf, size_t buflen, int *fdp,
+                           int numfds)
+{
+    struct msghdr msg;
+    struct iovec iov;
+    char ccmsg[CMSG_SPACE(sizeof(int)) * MAX_SEND_FDS];
+    struct cmsghdr *cmsg;
+    int res;
+
+    assert(numfds <= MAX_SEND_FDS);
+    iov.iov_base = buf;
+    iov.iov_len = buflen;
+
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = ccmsg;
+    msg.msg_controllen = sizeof(ccmsg);
+
+    res = recvmsg(sock, &msg, MSG_WAITALL);
+    if (!res)
+        return 0;
+    if (res == -1) {
+        perror("ulockmgr_server: recvmsg");
+        return -1;
+    }
+    if ((size_t) res != buflen) {
+        fprintf(stderr, "ulockmgr_server: short message received\n");
+        return -1;
+    }
+
+    cmsg = CMSG_FIRSTHDR(&msg);
+    if (cmsg) {
+        if (!cmsg->cmsg_type == SCM_RIGHTS) {
+            fprintf(stderr, "ulockmgr_server: unknown control message %d\n",
+                    cmsg->cmsg_type);
+            return -1;
+        }
+        memcpy(fdp, CMSG_DATA(cmsg), sizeof(int) * numfds);
+    }
+    return res;
+}
+
+static int closefrom(int minfd)
+{
+    DIR *dir = opendir("/proc/self/fd");
+    if (dir) {
+        int dfd = dirfd(dir);
+        struct dirent *ent;
+        while ((ent = readdir(dir))) {
+            char *end;
+            int fd = strtol(ent->d_name, &end, 10);
+            if (ent->d_name[0] && !end[0] && fd >= minfd && fd != dfd)
+                close(fd);
+        }
+        closedir(dir);
+    }
+    return 0;
+}
+
+static void send_reply(int cfd, struct message *msg)
+{
+    int res = send(cfd, msg, sizeof(struct message), MSG_NOSIGNAL);
+    if (res == -1)
+        perror("ulockmgr_server: sending reply");
+#ifdef DEBUG
+    fprintf(stderr, "ulockmgr_server: error: %i\n", msg->error);
+#endif
+}
+
+static void *process_request(void *d_)
+{
+    struct req_data *d = d_;
+    int res;
+
+    assert(d->msg.cmd == F_SETLKW);
+    res = fcntl(d->f->fd, F_SETLK, &d->msg.lock);
+    if (res == -1 && errno == EAGAIN) {
+        d->msg.error = EAGAIN;
+        d->msg.thr = pthread_self();
+        send_reply(d->cfd, &d->msg);
+        res = fcntl(d->f->fd, F_SETLKW, &d->msg.lock);
+    }
+    d->msg.error = (res == -1) ? errno : 0;
+    pthread_mutex_lock(&d->o->lock);
+    d->f->finished = 1;
+    pthread_mutex_unlock(&d->o->lock);
+    send_reply(d->cfd, &d->msg);
+    close(d->cfd);
+    free(d);
+
+    return NULL;
+}
+
+static void process_message(struct owner *o, struct message *msg, int cfd,
+                            int fd)
+{
+    struct fd_store *f;
+    struct req_data *d;
+    pthread_t tid;
+    int res;
+
+#ifdef DEBUG
+    fprintf(stderr, "ulockmgr_server: %i %i %i %lli %lli\n",
+            msg->cmd, msg->lock.l_type, msg->lock.l_whence, msg->lock.l_start,
+            msg->lock.l_len);
+#endif
+
+    if (msg->cmd == F_SETLK  && msg->lock.l_type == F_UNLCK && 
+        msg->lock.l_start == 0 && msg->lock.l_len == 0) {
+        struct fd_store **fp;
+
+        for (fp = &o->fds; *fp;) {
+            struct fd_store *f = *fp;
+            if (f->origfd == msg->fd && f->finished) {
+                close(f->fd);
+                *fp = f->next;
+                free(f);
+            } else
+                fp = &f->next;
+        }
+        close(fd);
+
+        msg->error = 0;
+        send_reply(cfd, msg);
+        close(cfd);
+        return;
+    }
+
+    f = malloc(sizeof(struct fd_store));
+    if (!f) {
+        msg->error = ENOLCK;
+        send_reply(cfd, msg);
+        close(cfd);
+        return;
+    }
+
+    f->fd = fd;
+    f->origfd = msg->fd;
+
+    if (msg->cmd == F_GETLK || msg->cmd == F_SETLK ||
+        msg->lock.l_type == F_UNLCK) {
+        res = fcntl(f->fd, msg->cmd, &msg->lock);
+        msg->error = (res == -1) ? errno : 0;
+        send_reply(cfd, msg);
+        close(cfd);
+        f->next = o->fds;
+        o->fds = f;
+        f->finished = 1;
+        return;
+    }
+
+    d = malloc(sizeof(struct req_data));
+    if (!d) {
+        msg->error = ENOLCK;
+        send_reply(cfd, msg);
+        close(cfd);
+        free(f);
+        return;
+    }
+
+    d->o = o;
+    d->cfd = cfd;
+    d->f = f;
+    d->msg = *msg;
+    res = pthread_create(&tid, NULL, process_request, d);
+    if (res) {
+        msg->error = ENOLCK;
+        send_reply(cfd, msg);
+        close(cfd);
+        free(d);
+        free(f);
+        return;
+    }
+
+    f->next = o->fds;
+    o->fds = f;
+    pthread_detach(tid);
+}
+
+static void sigusr1_handler(int sig)
+{
+    (void) sig;
+    /* Nothing to do */
+}
+
+static void process_owner(int cfd)
+{
+    struct owner o;
+    struct sigaction sa;
+
+    memset(&sa, 0, sizeof(struct sigaction));
+    sa.sa_handler = sigusr1_handler;
+    sigemptyset(&sa.sa_mask);
+
+    if (sigaction(SIGUSR1, &sa, NULL) == -1) {
+        perror("ulockmgr_server: cannot set sigusr1 signal handler");
+        exit(1);
+    }
+
+    memset(&o, 0, sizeof(struct owner));
+    pthread_mutex_init(&o.lock, NULL);
+    while (1) {
+        struct message msg;
+        int rfds[2];
+        int res;
+
+        res  = receive_message(cfd, &msg, sizeof(msg), rfds, 2);
+        if (!res)
+            break;
+        if (res == -1)
+            exit(1);
+
+        if (msg.intr)
+            pthread_kill(msg.thr, SIGUSR1);
+        else {
+            pthread_mutex_lock(&o.lock);
+            process_message(&o, &msg, rfds[0], rfds[1]);
+            pthread_mutex_unlock(&o.lock);
+        }
+    }
+    if (o.fds)
+        fprintf(stderr, "ulockmgr_server: open file descriptors on exit\n");
+}
+
+int main(int argc, char *argv[])
+{
+    int nullfd;
+    char *end;
+    int cfd;
+    sigset_t empty;
+
+    if (argc != 2 || !argv[1][0])
+        goto out_inval;
+
+    cfd = strtol(argv[1], &end, 10);
+    if (*end)
+        goto out_inval;
+
+    if (daemon(0, 1) == -1) {
+        perror("ulockmgr_server: daemon");
+        exit(1);
+    }
+
+    sigemptyset(&empty);
+    sigprocmask(SIG_SETMASK, &empty, NULL);
+
+    if (dup2(cfd, 4) == -1) {
+        perror("ulockmgr_server: dup2");
+        exit(1);
+    }
+    cfd = 4;
+    nullfd = open("/dev/null", O_RDWR);
+    dup2(nullfd, 0);
+    dup2(nullfd, 1);
+    close(3);
+    closefrom(5);
+    while (1) {
+        char c;
+        int sock;
+        int pid;
+        int res = receive_message(cfd, &c, sizeof(c), &sock, 1);
+        if (!res)
+            break;
+        if (res == -1)
+            exit(1);
+
+        pid = fork();
+        if (pid == -1) {
+            perror("ulockmgr_server: fork");
+            close(sock);
+            continue;
+        }
+        if (pid == 0) {
+            close(cfd);
+            pid = fork();
+            if (pid == -1) {
+                perror("ulockmgr_server: fork");
+                _exit(1);
+            }
+            if (pid == 0)
+                process_owner(sock);
+            _exit(0);
+        }
+        waitpid(pid, NULL, 0);
+        close(sock);
+    }
+    return 0;
+
+ out_inval:
+    fprintf(stderr, "%s should be started by libulockmgr\n", argv[0]);
+    return 1;
+}