file locking
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 20436ed..5076a5b 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -19,8 +19,31 @@
 #include <unistd.h>
 #include <limits.h>
 #include <errno.h>
+#include <pthread.h>
 
 #define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
+#define OFFSET_MAX 0x7fffffffffffffffLL
+
+struct fuse_ll;
+
+struct fuse_req {
+    struct fuse_ll *f;
+    uint64_t unique;
+    struct fuse_ctx ctx;
+    struct fuse_chan *ch;
+    int interrupted;
+    union {
+        struct {
+            uint64_t unique;
+        } i;
+        struct {
+            fuse_interrupt_func_t func;
+            void *data;
+        } ni;
+    } u;
+    struct fuse_req *next;
+    struct fuse_req *prev;
+};
 
 struct fuse_ll {
     int debug;
@@ -30,51 +53,24 @@
     void *userdata;
     uid_t owner;
     struct fuse_conn_info conn;
+    struct fuse_req list;
+    struct fuse_req interrupts;
+    pthread_mutex_t lock;
 };
 
-struct fuse_req {
-    struct fuse_ll *f;
-    uint64_t unique;
-    struct fuse_ctx ctx;
-    struct fuse_chan *ch;
-};
-
-static const char *opname(enum fuse_opcode opcode)
+#ifndef USE_UCLIBC
+#define mutex_init(mut) pthread_mutex_init(mut, NULL)
+#else
+static void mutex_init(pthread_mutex_t *mut)
 {
-    switch (opcode) {
-    case FUSE_LOOKUP:		return "LOOKUP";
-    case FUSE_FORGET:		return "FORGET";
-    case FUSE_GETATTR:		return "GETATTR";
-    case FUSE_SETATTR:		return "SETATTR";
-    case FUSE_READLINK:		return "READLINK";
-    case FUSE_SYMLINK:		return "SYMLINK";
-    case FUSE_MKNOD:		return "MKNOD";
-    case FUSE_MKDIR:		return "MKDIR";
-    case FUSE_UNLINK:		return "UNLINK";
-    case FUSE_RMDIR:		return "RMDIR";
-    case FUSE_RENAME:		return "RENAME";
-    case FUSE_LINK:		return "LINK";
-    case FUSE_OPEN:		return "OPEN";
-    case FUSE_READ:		return "READ";
-    case FUSE_WRITE:		return "WRITE";
-    case FUSE_STATFS:		return "STATFS";
-    case FUSE_FLUSH:		return "FLUSH";
-    case FUSE_RELEASE:		return "RELEASE";
-    case FUSE_FSYNC:		return "FSYNC";
-    case FUSE_SETXATTR:		return "SETXATTR";
-    case FUSE_GETXATTR:		return "GETXATTR";
-    case FUSE_LISTXATTR:	return "LISTXATTR";
-    case FUSE_REMOVEXATTR:	return "REMOVEXATTR";
-    case FUSE_INIT:		return "INIT";
-    case FUSE_OPENDIR:		return "OPENDIR";
-    case FUSE_READDIR:		return "READDIR";
-    case FUSE_RELEASEDIR:	return "RELEASEDIR";
-    case FUSE_FSYNCDIR:		return "FSYNCDIR";
-    case FUSE_ACCESS:		return "ACCESS";
-    case FUSE_CREATE:		return "CREATE";
-    default: 			return "???";
-    }
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_init(&attr);
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
+    pthread_mutex_init(mut, &attr);
+    pthread_mutexattr_destroy(&attr);
 }
+#endif
+
 
 static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
 {
@@ -120,8 +116,34 @@
     return ret;
 }
 
+static void list_init_req(struct fuse_req *req)
+{
+    req->next = req;
+    req->prev = req;
+}
+
+static void list_del_req(struct fuse_req *req)
+{
+    struct fuse_req *prev = req->prev;
+    struct fuse_req *next = req->next;
+    prev->next = next;
+    next->prev = prev;
+}
+
+static void list_add_req(struct fuse_req *req, struct fuse_req *next)
+{
+    struct fuse_req *prev = next->prev;
+    req->next = next;
+    req->prev = prev;
+    prev->next = req;
+    next->prev = req;
+}
+
 static void free_req(fuse_req_t req)
 {
+    pthread_mutex_lock(&req->f->lock);
+    list_del_req(req);
+    pthread_mutex_unlock(&req->f->lock);
     free(req);
 }
 
@@ -361,10 +383,27 @@
     return send_reply_ok(req, &arg, sizeof(arg));
 }
 
+int fuse_reply_lock(fuse_req_t req, struct flock *lock)
+{
+    struct fuse_lk_out arg;
+
+    memset(&arg, 0, sizeof(arg));
+    arg.lk.type = lock->l_type;
+    if (lock->l_type != F_UNLCK) {
+        arg.lk.start = lock->l_start;
+        if (lock->l_len == 0)
+            arg.lk.end = OFFSET_MAX;
+        else
+            arg.lk.end = lock->l_start + lock->l_len - 1;
+    }
+    arg.lk.pid = lock->l_pid;
+    return send_reply_ok(req, &arg, sizeof(arg));
+}
+
 static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
     char *name = (char *) inarg;
-    
+
     if (req->f->op.lookup)
         req->f->op.lookup(req, nodeid, name);
     else
@@ -374,7 +413,7 @@
 static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
     struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg;
-    
+
     if (req->f->op.forget)
         req->f->op.forget(req, nodeid, arg->nlookup);
 }
@@ -392,7 +431,7 @@
 static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
     struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg;
-    
+
     if (req->f->op.setattr) {
         struct fuse_file_info *fi = NULL;
         struct fuse_file_info fi_store;
@@ -414,7 +453,7 @@
 static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
     struct fuse_access_in *arg = (struct fuse_access_in *) inarg;
-    
+
     if (req->f->op.access)
         req->f->op.access(req, nodeid, arg->mask);
     else
@@ -434,7 +473,7 @@
 static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
     struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg;
-    
+
     if (req->f->op.mknod)
         req->f->op.mknod(req, nodeid, PARAM(arg), arg->mode, arg->rdev);
     else
@@ -444,7 +483,7 @@
 static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
     struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg;
-    
+
     if (req->f->op.mkdir)
         req->f->op.mkdir(req, nodeid, PARAM(arg), arg->mode);
     else
@@ -454,7 +493,7 @@
 static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
     char *name = (char *) inarg;
-    
+
     if (req->f->op.unlink)
         req->f->op.unlink(req, nodeid, name);
     else
@@ -464,7 +503,7 @@
 static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
     char *name = (char *) inarg;
-    
+
     if (req->f->op.rmdir)
         req->f->op.rmdir(req, nodeid, name);
     else
@@ -475,7 +514,7 @@
 {
     char *name = (char *) inarg;
     char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1;
-    
+
     if (req->f->op.symlink)
         req->f->op.symlink(req, linkname, nodeid, name);
     else
@@ -497,7 +536,7 @@
 static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
     struct fuse_link_in *arg = (struct fuse_link_in *) inarg;
-    
+
     if (req->f->op.link)
         req->f->op.link(req, arg->oldnodeid, nodeid, PARAM(arg));
     else
@@ -507,7 +546,7 @@
 static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
     struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
-    
+
     if (req->f->op.create) {
         struct fuse_file_info fi;
 
@@ -536,7 +575,7 @@
 static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
     struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
-    
+
     if (req->f->op.read) {
         struct fuse_file_info fi;
 
@@ -568,13 +607,17 @@
 {
     struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg;
     struct fuse_file_info fi;
+    uint64_t lock_owner = 0;
+
+    if (req->f->conn.proto_minor >= 7)
+        lock_owner = arg->lock_owner;
 
     memset(&fi, 0, sizeof(fi));
     fi.fh = arg->fh;
     fi.fh_old = fi.fh;
 
     if (req->f->op.flush)
-        req->f->op.flush(req, nodeid, &fi);
+        req->f->op.flush(req, nodeid, &fi, lock_owner);
     else
         fuse_reply_err(req, ENOSYS);
 }
@@ -702,7 +745,7 @@
 static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
     struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
-    
+
     if (req->f->op.getxattr)
         req->f->op.getxattr(req, nodeid, PARAM(arg), arg->size);
     else
@@ -712,7 +755,7 @@
 static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
     struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
-    
+
     if (req->f->op.listxattr)
         req->f->op.listxattr(req, nodeid, arg->size);
     else
@@ -722,13 +765,126 @@
 static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
     char *name = (char *) inarg;
-    
+
     if (req->f->op.removexattr)
         req->f->op.removexattr(req, nodeid, name);
     else
         fuse_reply_err(req, ENOSYS);
 }
 
+static void convert_fuse_file_lock(struct fuse_file_lock *fl,
+                                   struct flock *flock)
+{
+    memset(flock, 0, sizeof(struct flock));
+    flock->l_type = fl->type;
+    flock->l_whence = SEEK_SET;
+    flock->l_start = fl->start;
+    if (fl->end == OFFSET_MAX)
+        flock->l_len = 0;
+    else
+        flock->l_len = fl->end - fl->start + 1;
+    flock->l_pid = fl->pid;
+}
+
+static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+    struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
+    struct fuse_file_info fi;
+    struct flock flock;
+
+    memset(&fi, 0, sizeof(fi));
+    fi.fh = arg->fh;
+
+    convert_fuse_file_lock(&arg->lk, &flock);
+    if (req->f->op.getlk)
+        req->f->op.getlk(req, nodeid, &fi, &flock, arg->owner);
+    else
+        fuse_reply_err(req, ENOSYS);
+}
+
+static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid,
+                            const void *inarg, int sleep)
+{
+    struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
+    struct fuse_file_info fi;
+    struct flock flock;
+
+    memset(&fi, 0, sizeof(fi));
+    fi.fh = arg->fh;
+
+    convert_fuse_file_lock(&arg->lk, &flock);
+    if (req->f->op.setlk)
+        req->f->op.setlk(req, nodeid, &fi, &flock, arg->owner, sleep);
+    else
+        fuse_reply_err(req, ENOSYS);
+}
+
+static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+    do_setlk_common(req, nodeid, inarg, 0);
+}
+
+static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+    do_setlk_common(req, nodeid, inarg, 1);
+}
+
+static int find_interrupted(struct fuse_ll *f, struct fuse_req *req)
+{
+    struct fuse_req *curr;
+
+    for (curr = f->list.next; curr != &f->list; curr = curr->next) {
+        if (curr->unique == req->u.i.unique) {
+            curr->interrupted = 1;
+            if (curr->u.ni.func)
+                curr->u.ni.func(curr, curr->u.ni.data);
+            return 1;
+        }
+    }
+    for (curr = f->interrupts.next; curr != &f->interrupts; curr = curr->next) {
+        if (curr->u.i.unique == req->u.i.unique)
+            return 1;
+    }
+    return 0;
+}
+
+static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+    struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg;
+    struct fuse_ll *f = req->f;
+
+    (void) nodeid;
+    if (f->debug) {
+        printf("INTERRUPT: %llu\n", (unsigned long long) arg->unique);
+        fflush(stdout);
+    }
+
+    req->u.i.unique = arg->unique;
+
+    pthread_mutex_lock(&f->lock);
+    if (find_interrupted(f, req))
+        free(req);
+    else
+        list_add_req(req, &f->interrupts);
+    pthread_mutex_unlock(&f->lock);
+}
+
+static void check_interrupt(struct fuse_ll *f, struct fuse_req *req)
+{
+    struct fuse_req *curr;
+
+    for (curr = f->interrupts.next; curr != &f->interrupts; curr = curr->next) {
+        if (curr->u.i.unique == req->unique) {
+            list_del_req(curr);
+            free(curr);
+            return;
+        }
+    }
+    curr = f->interrupts.next;
+    if (curr != &f->interrupts)
+        fuse_reply_err(curr, EAGAIN);
+}
+
 static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
     struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
@@ -776,6 +932,8 @@
     outarg.minor = FUSE_KERNEL_MINOR_VERSION;
     if (f->conn.async_read)
         outarg.flags |= FUSE_ASYNC_READ;
+    if (f->op.getlk && f->op.setlk)
+        outarg.flags |= FUSE_POSIX_LOCKS;
     outarg.max_readahead = f->conn.max_readahead;
     outarg.max_write = f->conn.max_write;
 
@@ -800,39 +958,69 @@
     return &req->ctx;
 }
 
-static void (*fuse_ll_ops[FUSE_MAXOP])(fuse_req_t, fuse_ino_t, const void *) = {
-    [FUSE_LOOKUP]      = do_lookup,
-    [FUSE_FORGET]      = do_forget,
-    [FUSE_GETATTR]     = do_getattr,
-    [FUSE_SETATTR]     = do_setattr,
-    [FUSE_READLINK]    = do_readlink,
-    [FUSE_SYMLINK]     = do_symlink,
-    [FUSE_MKNOD]       = do_mknod,
-    [FUSE_MKDIR]       = do_mkdir,
-    [FUSE_UNLINK]      = do_unlink,
-    [FUSE_RMDIR]       = do_rmdir,
-    [FUSE_RENAME]      = do_rename,
-    [FUSE_LINK]        = do_link,
-    [FUSE_OPEN]        = do_open,
-    [FUSE_READ]        = do_read,
-    [FUSE_WRITE]       = do_write,
-    [FUSE_STATFS]      = do_statfs,
-    [FUSE_RELEASE]     = do_release,
-    [FUSE_FSYNC]       = do_fsync,
-    [FUSE_SETXATTR]    = do_setxattr,
-    [FUSE_GETXATTR]    = do_getxattr,
-    [FUSE_LISTXATTR]   = do_listxattr,
-    [FUSE_REMOVEXATTR] = do_removexattr,
-    [FUSE_FLUSH]       = do_flush,
-    [FUSE_INIT]        = do_init,
-    [FUSE_OPENDIR]     = do_opendir,
-    [FUSE_READDIR]     = do_readdir,
-    [FUSE_RELEASEDIR]  = do_releasedir,
-    [FUSE_FSYNCDIR]    = do_fsyncdir,
-    [FUSE_ACCESS]      = do_access,
-    [FUSE_CREATE]      = do_create,
+void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func,
+                             void *data)
+{
+    struct fuse_ll *f = req->f;
+
+    pthread_mutex_lock(&f->lock);
+    req->u.ni.func = func;
+    req->u.ni.data = data;
+    if (req->interrupted && func)
+        func(req, data);
+    pthread_mutex_unlock(&f->lock);
+}
+
+static struct {
+    void (*func)(fuse_req_t, fuse_ino_t, const void *);
+    const char *name;
+} fuse_ll_ops[] = {
+    [FUSE_LOOKUP]      = { do_lookup,      "LOOKUP"      },
+    [FUSE_FORGET]      = { do_forget,      "FORGET"      },
+    [FUSE_GETATTR]     = { do_getattr,     "GETATTR"     },
+    [FUSE_SETATTR]     = { do_setattr,     "SETATTR"     },
+    [FUSE_READLINK]    = { do_readlink,    "READLINK"    },
+    [FUSE_SYMLINK]     = { do_symlink,     "SYMLINK"     },
+    [FUSE_MKNOD]       = { do_mknod,       "MKNOD"       },
+    [FUSE_MKDIR]       = { do_mkdir,       "MKDIR"       },
+    [FUSE_UNLINK]      = { do_unlink,      "UNLINK"      },
+    [FUSE_RMDIR]       = { do_rmdir,       "RMDIR"       },
+    [FUSE_RENAME]      = { do_rename,      "RENAME"      },
+    [FUSE_LINK]        = { do_link,        "LINK"        },
+    [FUSE_OPEN]        = { do_open,        "OPEN"        },
+    [FUSE_READ]        = { do_read,        "READ"        },
+    [FUSE_WRITE]       = { do_write,       "WRITE"       },
+    [FUSE_STATFS]      = { do_statfs,      "STATFS"      },
+    [FUSE_RELEASE]     = { do_release,     "RELEASE"     },
+    [FUSE_FSYNC]       = { do_fsync,       "FSYNC"       },
+    [FUSE_SETXATTR]    = { do_setxattr,    "SETXATTR"    },
+    [FUSE_GETXATTR]    = { do_getxattr,    "GETXATTR"    },
+    [FUSE_LISTXATTR]   = { do_listxattr,   "LISTXATTR"   },
+    [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
+    [FUSE_FLUSH]       = { do_flush,       "FLUSH"       },
+    [FUSE_INIT]        = { do_init,        "INIT"        },
+    [FUSE_OPENDIR]     = { do_opendir,     "OPENDIR"     },
+    [FUSE_READDIR]     = { do_readdir,     "READDIR"     },
+    [FUSE_RELEASEDIR]  = { do_releasedir,  "RELEASEDIR"  },
+    [FUSE_FSYNCDIR]    = { do_fsyncdir,    "FSYNCDIR"    },
+    [FUSE_GETLK]       = { do_getlk,       "GETLK"       },
+    [FUSE_SETLK]       = { do_setlk,       "SETLK"       },
+    [FUSE_SETLKW]      = { do_setlkw,      "SETLKW"      },
+    [FUSE_ACCESS]      = { do_access,      "ACCESS"      },
+    [FUSE_CREATE]      = { do_create,      "CREATE"      },
+    [FUSE_INTERRUPT]   = { do_interrupt,   "INTERRUPT"   },
 };
 
+#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
+
+static const char *opname(enum fuse_opcode opcode)
+{
+    if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
+        return "???";
+    else
+        return fuse_ll_ops[opcode].name;
+}
+
 static void fuse_ll_process(void *data, const char *buf, size_t len,
                      struct fuse_chan *ch)
 {
@@ -843,12 +1031,13 @@
 
     if (f->debug) {
         printf("unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %i\n",
-               in->unique, opname((enum fuse_opcode) in->opcode), in->opcode,
+               (unsigned long long) in->unique,
+               opname((enum fuse_opcode) in->opcode), in->opcode,
                (unsigned long) in->nodeid, len);
         fflush(stdout);
     }
 
-    req = (struct fuse_req *) malloc(sizeof(struct fuse_req));
+    req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
     if (req == NULL) {
         fprintf(stderr, "fuse: failed to allocate request\n");
         return;
@@ -860,6 +1049,7 @@
     req->ctx.gid = in->gid;
     req->ctx.pid = in->pid;
     req->ch = ch;
+    list_init_req(req);
 
     if (!f->got_init && in->opcode != FUSE_INIT)
         fuse_reply_err(req, EIO);
@@ -869,10 +1059,17 @@
              in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
              in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR) {
         fuse_reply_err(req, EACCES);
-    } else if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode])
+    } else if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
         fuse_reply_err(req, ENOSYS);
-    else
-        fuse_ll_ops[in->opcode](req, in->nodeid, inarg);
+    else {
+        if (in->opcode != FUSE_INTERRUPT) {
+            pthread_mutex_lock(&f->lock);
+            check_interrupt(f, req);
+            list_add_req(req, &f->list);
+            pthread_mutex_unlock(&f->lock);
+        }
+        fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
+    }
 }
 
 enum {
@@ -977,6 +1174,9 @@
     f->conn.async_read = 1;
     f->conn.max_write = UINT_MAX;
     f->conn.max_readahead = UINT_MAX;
+    list_init_req(&f->list);
+    list_init_req(&f->interrupts);
+    mutex_init(&f->lock);
 
     if (fuse_opt_parse(args, f, fuse_ll_opts, fuse_ll_opt_proc) == -1)
         goto out_free;