interrupted request improvements
authorMiklos Szeredi <miklos@szeredi.hu>
Tue, 4 Jan 2005 12:45:54 +0000 (12:45 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Tue, 4 Jan 2005 12:45:54 +0000 (12:45 +0000)
12 files changed:
ChangeLog
Filesystems
kernel/dev.c
kernel/dir.c
kernel/file.c
kernel/fuse_i.h
kernel/fuse_kernel.h
kernel/inode.c
lib/fuse.c
lib/fuse_i.h
lib/fuse_mt.c
lib/helper.c

index 7b46d61aef877f80ae8a93a4c3bf1332be039cae..0395c389a25c5ca422d42f23a2ad6e743c37d98f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2005-01-04  Miklos Szeredi <miklos@szeredi.hu>
+
+       * KERNEL: if request is interrupted, still keep reference to used
+       inode(s) and file, so that FORGET and RELEASE are not sent until
+       userspace finishes the request.
+
+2004-12-16  Miklos Szeredi <miklos@szeredi.hu>
+
+       * KERNEL ABI: update interface to make it independent of type
+       sizes.  This will help on 64 bit architectures which can run
+       legacy 32 bit applications.
+
+       * KERNEL ABI: add "len" field to request headers.  This will allow
+       sending/receiving requests in multiple chunks.
+
+       * KERNEL: handle file type change more intelligently
+
+       * LIB: "-o debug" option should disable backgrounding (fix by
+       Fabien Reygrobellet)
+
 2004-12-13  Miklos Szeredi <miklos@szeredi.hu>
 
        * KERNEL: invalidate dentry/attributes if interrupted request
index 553b714172f176fd4ad3d5cde25f1fc85962c2cb..6e147e5c30093b8a6f50fe6fe33afc2e67007336 100644 (file)
@@ -1,6 +1,6 @@
 Name: OW
 
-Author: Paul H. Alfille     palfille at partners org
+Author: Paul H. Alfille / palfille at partners org
 
 Homepage: http://owfs.sourceforge.net
 
@@ -12,7 +12,7 @@ Description:
 ==============================================================================
 Name: FunFS (status: alpha)
 
-Author: Michael Grigoriev (Net Integration Technologies)     mag at luminal org
+Author: Michael Grigoriev (Net Integration Technologies) / mag at luminal org
 
 Homepage: http://www.luminal.org/wiki/index.php/FunFS/FunFS
 
@@ -24,7 +24,7 @@ Description:
 ==============================================================================
 Name: EncFS
 
-Author:  Valient Gough     vgough at pobox com
+Author:  Valient Gough / vgough at pobox com
 
 Homepage: http://pobox.com/~vgough/encfs.html
 
@@ -37,10 +37,12 @@ Description:
 ==============================================================================
 Name: FUSE-J
 
-Author: Peter Levart     peter.levart at select-tech si
+Author: Peter Levart / peter.levart at select-tech si
 
 Download: http://www.select-tech.si/fuse/
 
+Alternate download: http://www.cl.cam.ac.uk/~tdm25/fuse-j/
+
 Description: 
 
   FUSE-J provides Java binding for FUSE.  It comes with the
@@ -49,7 +51,7 @@ Description:
 ==============================================================================
 Name: SMB for FUSE
 
-Author: Vincent Wagelaar     vincent at ricardis tudelft nl
+Author: Vincent Wagelaar / vincent at ricardis tudelft nl
 
 Homepage: http://hannibal.lr-s.tudelft.nl/~vincent/fusesmb/
 
@@ -61,7 +63,7 @@ Description:
 ==============================================================================
 Name: Run-Time-Access
 
-Author: Bob Smith     bsmith at linuxtoys org
+Author: Bob Smith / bsmith at linuxtoys org
 
 Homepage: http://www.runtimeaccess.com
 
@@ -76,7 +78,7 @@ Description:
 ==============================================================================
 Name: PhoneBook
 
-Author: David McNab     david at rebirthing co nz
+Author: David McNab / david at rebirthing co nz
 
 Homepage: http://www.freenet.org.nz/phonebook
 
@@ -91,7 +93,7 @@ Description:
 ==============================================================================
 Name: KIO Fuse Gateway
 
-Author: Alexander Neundorf     neundorf at kde org
+Author: Alexander Neundorf / neundorf at kde org
 
 Homepage: http://kde.ground.cz/tiki-index.php?page=KIO+Fuse+Gateway
 
@@ -104,7 +106,7 @@ Description:
 ==============================================================================
 Name: C# bindings
 
-Author: Valient Gough     vgough at pobox com
+Author: Valient Gough / vgough at pobox com
 
 Homepage: http://pobox.com/~vgough/fuse-csharp.html
 
@@ -117,7 +119,7 @@ Description:
 ==============================================================================
 Name: LUFS bridge (alpha)
 
-Author: Miklos Szeredi     miklos at szeredi hu
+Author: Miklos Szeredi / miklos at szeredi hu
 
 Homepage: http://sourceforge.net/project/showfiles.php?group_id=121684&package_id=132803
 
@@ -130,7 +132,7 @@ Description:
 ==============================================================================
 Name: btfs (Bluetooth FileSystemMapping)
 
-Author: Collin R. Mulliner     collin at betaversion net
+Author: Collin R. Mulliner / collin at betaversion net
 
 Homepage: http://www.mulliner.org/bluetooth/btfs.php
 
@@ -144,7 +146,7 @@ Description:
 ==============================================================================
 Name: mcachefs
 
-Author: Michael Still     mikal at stillhq com
+Author: Michael Still / mikal at stillhq com
 
 Homepage: http://lists.samba.org/archive/linux/2004-March/010211.html
 
@@ -159,7 +161,7 @@ Description:
 ==============================================================================
 Name: Fusedav
 
-Author: Lennart Poettering     mzshfrqni at 0pointer de
+Author: Lennart Poettering / mzshfrqni at 0pointer de
 
 Homepage: http://0pointer.de/lennart/projects/fusedav/
 
@@ -172,7 +174,7 @@ Description:
 ==============================================================================
 Name: RelFS
 
-Author: Vincenzo Ciancia     vincenzo_ml at yahoo it
+Author: Vincenzo Ciancia / vincenzo_ml at yahoo it
 
 Homepage: http://relfs.sourceforge.net/
 
@@ -186,7 +188,7 @@ Description:
 ==============================================================================
 Name: GmailFS
 
-Author: Richard Jones     richard at jones name
+Author: Richard Jones / richard at jones name
 
 Homepage: http://richard.jones.name/google-hacks/gmail-filesystem/gmail-filesystem.html
 
@@ -200,7 +202,7 @@ Description:
 ==============================================================================
 Name: DataDraw
 
-Author: Bill Cox     bill at viasic com
+Author: Bill Cox / bill at viasic com
 
 Homepage: http://www.viasic.com/opensource/
 
@@ -212,7 +214,7 @@ Description:
 ==============================================================================
 Name: gphoto2-fuse-fs
 
-Author: Christopher Lester     lester at hep phy cam ac uk
+Author: Christopher Lester / lester at hep phy cam ac uk
 
 Homepage: http://www.hep.phy.cam.ac.uk/~lester/gphoto2-fuse-fs/
 
@@ -226,7 +228,7 @@ Description:
 ==============================================================================
 Name: cvsfs-fuse
 
-Author: Patrick Frank     pfrank at gmx de
+Author: Patrick Frank / pfrank at gmx de
 
 Homepage: http://sourceforge.net/projects/cvsfs
 
@@ -240,7 +242,7 @@ Description:
 ==============================================================================
 Name: Wayback (User-level Versioning File System for Linux)
 
-Author: Brian Cornell     techie at northwestern edu
+Author: Brian Cornell / techie at northwestern edu
 
 Homepage: http://wayback.sourceforge.net/
 
@@ -254,3 +256,18 @@ Description:
   directory.
 
 ==============================================================================
+Name: Trivial Rolebased Authorisation & Capability Statemachine (TRACS)
+
+Author: Rob J Meijer / rmeijer at xs4all nl
+
+Homepage: http://www.xs4all.nl/~rmeijer/tracs.html
+
+Description:
+
+  This project is the first spin-off project of the Security Incident
+  Policy Enforcement System project. In the process of designing a
+  SIPES, the need was recognized for the implementation of an
+  authorisation server that provides functionality not provided by any
+  of the current authorisation solutions.
+
+==============================================================================
index 3f2d85a0a0ddbda98b40d512b3634eda8967b1a1..bfd2bd767dab5c5bcfea1f4dbb520ff2f15a439a 100644 (file)
@@ -55,15 +55,6 @@ void fuse_request_free(struct fuse_req *req)
        kmem_cache_free(fuse_req_cachep, req);
 }
 
-static int get_unique(struct fuse_conn *fc)
-{
-       fc->reqctr++;
-       /* zero is special */
-       if (fc->reqctr == 0)
-               fc->reqctr = 1;
-       return fc->reqctr;
-}
-
 #ifdef KERNEL_2_6
 static inline void block_sigs(sigset_t *oldset)
 {
@@ -112,6 +103,7 @@ static void __fuse_get_request(struct fuse_req *req)
 /* Must be called with > 1 refcount */
 static void __fuse_put_request(struct fuse_req *req)
 {
+       BUG_ON(atomic_read(&req->count) < 2);
        atomic_dec(&req->count);
 }
 
@@ -134,7 +126,7 @@ static struct fuse_req *do_get_request(struct fuse_conn *fc)
 
 struct fuse_req *fuse_get_request(struct fuse_conn *fc)
 {
-       if (down_interruptible(&fc->unused_sem))
+       if (down_interruptible(&fc->outstanding_sem))
                return NULL;
        return  do_get_request(fc);
 }
@@ -145,7 +137,7 @@ struct fuse_req *fuse_get_request_nonint(struct fuse_conn *fc)
        sigset_t oldset;
 
        block_sigs(&oldset);
-       intr = down_interruptible(&fc->unused_sem);
+       intr = down_interruptible(&fc->outstanding_sem);
        restore_sigs(&oldset);
        return intr ? NULL : do_get_request(fc);
 }
@@ -154,12 +146,16 @@ void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req)
 {
        if (!req->preallocated)
                fuse_request_free(req);
-       else {
-               spin_lock(&fuse_lock);
+
+       spin_lock(&fuse_lock);
+       if (req->preallocated)
                list_add(&req->list, &fc->unused_list);
-               spin_unlock(&fuse_lock);
-               up(&fc->unused_sem);
-       }
+
+       if (fc->outstanding_debt)
+               fc->outstanding_debt--;
+       else
+               up(&fc->outstanding_sem);
+       spin_unlock(&fuse_lock);
 }
 
 void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
@@ -175,6 +171,14 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
        req->finished = 1;
        putback = atomic_dec_and_test(&req->count);
        spin_unlock(&fuse_lock);
+       if (req->background) {
+               if (req->inode)
+                       iput(req->inode);
+               if (req->inode2)
+                       iput(req->inode2);
+               if (req->file)
+                       fput(req->file);
+       }
        wake_up(&req->waitq);
        if (putback)
                fuse_putback_request(fc, req);
@@ -190,9 +194,21 @@ static int request_wait_answer_nonint(struct fuse_req *req)
        return err;
 }
 
-/* Called with fuse_lock held.  Releases, and then reaquires it. */
-static void request_wait_answer(struct fuse_req *req, int interruptible,
-                               int background)
+static void background_request(struct fuse_req *req)
+{
+       /* Need to get hold of the inode(s) and/or file used in the
+          request, so FORGET and RELEASE are not sent too early */
+       req->background = 1;
+       if (req->inode)
+               req->inode = igrab(req->inode);
+       if (req->inode2)
+               req->inode2 = igrab(req->inode2);
+       if (req->file)
+               get_file(req->file);
+}
+
+/* Called with fuse_lock held.  Releases, and then reacquires it. */
+static void request_wait_answer(struct fuse_req *req, int interruptible)
 {
        int intr;
 
@@ -212,10 +228,6 @@ static void request_wait_answer(struct fuse_req *req, int interruptible,
        if (!intr)
                return;
 
-       if (background && !req->sent) {
-               req->isreply = 0;
-               return;
-       }
        if (!interruptible || req->sent)
                req->out.h.error = -EINTR;
        else
@@ -232,50 +244,74 @@ static void request_wait_answer(struct fuse_req *req, int interruptible,
                wait_event(req->waitq, !req->locked);
                spin_lock(&fuse_lock);
        }
-       if (!list_empty(&req->list)) {
-               /* request is still on one of the lists */
+       if (!req->sent && !list_empty(&req->list)) {
                list_del(&req->list);
                __fuse_put_request(req);
+       } else if (req->sent)
+               background_request(req);
+}
+
+static unsigned len_args(unsigned numargs, struct fuse_arg *args)
+{
+       unsigned nbytes = 0;
+       unsigned i;
+
+       for (i = 0; i < numargs; i++)
+               nbytes += args[i].size;
+
+       return nbytes;
+}
+
+static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
+{
+       fc->reqctr++;
+       /* zero is special */
+       if (fc->reqctr == 0)
+               fc->reqctr = 1;
+       req->in.h.unique = fc->reqctr;
+       req->in.h.len = sizeof(struct fuse_in_header) + 
+               len_args(req->in.numargs, (struct fuse_arg *) req->in.args);
+       if (!req->preallocated) {
+               /* decrease outstanding_sem, but without blocking... */
+               if (down_trylock(&fc->outstanding_sem))
+                       fc->outstanding_debt++;
        }
+       list_add_tail(&req->list, &fc->pending);
+       wake_up(&fc->waitq);
 }
 
 static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req,
-                             int interruptible, int background)
+                             int interruptible)
 {
        req->isreply = 1;
        spin_lock(&fuse_lock);
        req->out.h.error = -ENOTCONN;
        if (fc->file) {
-               req->in.h.unique = get_unique(fc);
-               list_add_tail(&req->list, &fc->pending);
-               wake_up(&fc->waitq);
+               queue_request(fc, req);
                /* acquire extra reference, since request is still needed
                   after request_end() */
                __fuse_get_request(req);
 
-               request_wait_answer(req, interruptible, background);
+               request_wait_answer(req, interruptible);
        }
        spin_unlock(&fuse_lock);
 }
 
 void request_send(struct fuse_conn *fc, struct fuse_req *req)
 {
-       request_send_wait(fc, req, 1, 0);
+       request_send_wait(fc, req, 1);
 }
 
-void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req,
-                        int background)
+void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req)
 {
-       request_send_wait(fc, req, 0, background);
+       request_send_wait(fc, req, 0);
 }
 
-void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
+void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
 {
-       req->isreply = 0;
        spin_lock(&fuse_lock);
        if (fc->file) {
-               list_add_tail(&req->list, &fc->pending);
-               wake_up(&fc->waitq);
+               queue_request(fc, req);
                spin_unlock(&fuse_lock);
        } else {
                req->out.h.error = -ENOTCONN;
@@ -283,6 +319,19 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
        }
 }
 
+void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
+{
+       req->isreply = 0;
+       request_send_nowait(fc, req);
+}
+
+void request_send_background(struct fuse_conn *fc, struct fuse_req *req)
+{
+       req->isreply = 1;
+       background_request(req);
+       request_send_nowait(fc, req);
+}
+
 static inline int lock_request(struct fuse_req *req)
 {
        int err = 0;
@@ -486,17 +535,6 @@ static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs,
        return err;
 }
 
-static unsigned len_args(unsigned numargs, struct fuse_arg *args)
-{
-       unsigned nbytes = 0;
-       unsigned i;
-
-       for (i = 0; i < numargs; i++)
-               nbytes += args[i].size;
-
-       return nbytes;
-}
-
 static void request_wait(struct fuse_conn *fc)
 {
        DECLARE_WAITQUEUE(wait, current);
@@ -544,8 +582,7 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
        spin_unlock(&fuse_lock);
 
        in = &req->in;
-       reqsize = sizeof(struct fuse_in_header);
-       reqsize += len_args(in->numargs, (struct fuse_arg *) in->args);
+       reqsize = req->in.h.len;
        nbytes = fuse_copy_init(&cs, 1, req, iov, nr_segs);
        err = -EINVAL;
        if (nbytes >= reqsize) {
@@ -652,16 +689,22 @@ static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov,
        if (err)
                goto err_finish;
        err = -EINVAL;
-       if (!oh.unique || oh.error <= -1000 || oh.error > 0)
+       if (!oh.unique || oh.error <= -1000 || oh.error > 0 || 
+           oh.len != nbytes)
                goto err_finish;
 
        spin_lock(&fuse_lock);
        req = request_find(fc, oh.unique);
-       err = -ENOENT;
+       err = -EINVAL;
        if (!req)
                goto err_unlock;
 
        list_del_init(&req->list);
+       if (req->interrupted) {
+               request_end(fc, req);
+               fuse_copy_finish(&cs);
+               return -ENOENT;
+       }
        req->out.h = oh;
        req->locked = 1;
        cs.req = req;
index 19aebb19667fca876b0fe87a8117dd632e75cc32..08427a9270b572927bb9680db4c1a9c6c650d3ac 100644 (file)
 #endif
 #include <linux/sched.h>
 
-static struct inode_operations fuse_dir_inode_operations;
-static struct inode_operations fuse_file_inode_operations;
-static struct inode_operations fuse_symlink_inode_operations;
-static struct file_operations fuse_dir_operations;
-static struct dentry_operations fuse_dentry_operations;
-
-#ifndef KERNEL_2_6
-#define new_decode_dev(x) (x)
-#define new_encode_dev(x) (x)
-#endif
-static void change_attributes(struct inode *inode, struct fuse_attr *attr)
-{
-       if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size) {
-#ifdef KERNEL_2_6
-               invalidate_inode_pages(inode->i_mapping);
-#else
-               invalidate_inode_pages(inode);
-#endif
-       }
-
-       inode->i_ino     = attr->ino;
-       inode->i_mode    = (inode->i_mode & S_IFMT) + (attr->mode & 07777);
-       inode->i_nlink   = attr->nlink;
-       inode->i_uid     = attr->uid;
-       inode->i_gid     = attr->gid;
-       i_size_write(inode, attr->size);
-       inode->i_blksize = PAGE_CACHE_SIZE;
-       inode->i_blocks  = attr->blocks;
-#ifdef KERNEL_2_6
-       inode->i_atime.tv_sec   = attr->atime;
-       inode->i_atime.tv_nsec  = attr->atimensec;
-       inode->i_mtime.tv_sec   = attr->mtime;
-       inode->i_mtime.tv_nsec  = attr->mtimensec;
-       inode->i_ctime.tv_sec   = attr->ctime;
-       inode->i_ctime.tv_nsec  = attr->ctimensec;
-#else
-       inode->i_atime   = attr->atime;
-       inode->i_mtime   = attr->mtime;
-       inode->i_ctime   = attr->ctime;
-#endif
-}
-
-static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
-{
-       inode->i_mode = attr->mode & S_IFMT;
-       i_size_write(inode, attr->size);
-       if (S_ISREG(inode->i_mode)) {
-               inode->i_op = &fuse_file_inode_operations;
-               fuse_init_file_inode(inode);
-       }
-       else if (S_ISDIR(inode->i_mode)) {
-               inode->i_op = &fuse_dir_inode_operations;
-               inode->i_fop = &fuse_dir_operations;
-       }
-       else if (S_ISLNK(inode->i_mode)) {
-               inode->i_op = &fuse_symlink_inode_operations;
-       }
-       else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
-                S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
-               inode->i_op = &fuse_file_inode_operations;
-               init_special_inode(inode, inode->i_mode,
-                                  new_decode_dev(attr->rdev));
-       } else {
-               /* Don't let user create weird files */
-               inode->i_mode = S_IFREG;
-               inode->i_op = &fuse_file_inode_operations;
-               fuse_init_file_inode(inode);
-       }
-}
-
-#ifdef KERNEL_2_6
-static int fuse_inode_eq(struct inode *inode, void *_nodeidp)
-{
-       unsigned long nodeid = *(unsigned long *) _nodeidp;
-       if (get_node_id(inode) == nodeid)
-               return 1;
-       else
-               return 0;
-}
-
-static int fuse_inode_set(struct inode *inode, void *_nodeidp)
-{
-       unsigned long nodeid = *(unsigned long *) _nodeidp;
-       get_fuse_inode(inode)->nodeid = nodeid;
-       return 0;
-}
-
-struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
-                       int generation, struct fuse_attr *attr, int version)
-{
-       struct inode *inode;
-       struct fuse_conn *fc = get_fuse_conn_super(sb);
-
-       inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid);
-       if (!inode)
-               return NULL;
-
-       if ((inode->i_state & I_NEW)) {
-               inode->i_generation = generation;
-               inode->i_data.backing_dev_info = &fc->bdi;
-               fuse_init_inode(inode, attr);
-               unlock_new_inode(inode);
-       }
-
-       change_attributes(inode, attr);
-       inode->i_version = version;
-       return inode;
-}
-
-struct inode *fuse_ilookup(struct super_block *sb, unsigned long nodeid)
+static inline unsigned long time_to_jiffies(unsigned long sec,
+                                           unsigned long nsec)
 {
-       return ilookup5(sb, nodeid, fuse_inode_eq, &nodeid);
-}
-#else
-static int fuse_inode_eq(struct inode *inode, unsigned long ino, void *_nodeidp){
-       unsigned long nodeid = *(unsigned long *) _nodeidp;
-       if (inode->u.generic_ip && get_node_id(inode) == nodeid)
-               return 1;
-       else
+       /* prevent wrapping of jiffies */
+       if (sec + 1 >= LONG_MAX / HZ)
                return 0;
-}
-
-struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
-                       int generation, struct fuse_attr *attr, int version)
-{
-       struct inode *inode;
-
-       inode = iget4(sb, attr->ino, fuse_inode_eq, &nodeid);
-       if (!inode)
-               return NULL;
 
-       if (!inode->u.generic_ip) {
-               get_fuse_inode(inode)->nodeid = nodeid;
-               inode->u.generic_ip = inode;
-               inode->i_generation = generation;
-               fuse_init_inode(inode, attr);
-       }
-
-       change_attributes(inode, attr);
-       inode->i_version = version;
-       return inode;
-}
-
-struct inode *fuse_ilookup(struct super_block *sb, ino_t ino, unsigned long nodeid)
-{
-       struct inode *inode = iget4(sb, ino, fuse_inode_eq, &nodeid);
-       if (inode && !inode->u.generic_ip) {
-               iput(inode);
-               inode = NULL;
-       }
-       return inode;
+       return jiffies + sec * HZ + nsec / (1000000000 / HZ);
 }
-#endif
 
 static void fuse_lookup_init(struct fuse_req *req, struct inode *dir,
                             struct dentry *entry,
@@ -177,6 +33,7 @@ static void fuse_lookup_init(struct fuse_req *req, struct inode *dir,
 {
        req->in.h.opcode = FUSE_LOOKUP;
        req->in.h.nodeid = get_node_id(dir);
+       req->inode = dir;
        req->in.numargs = 1;
        req->in.args[0].size = entry->d_name.len + 1;
        req->in.args[0].value = entry->d_name.name;
@@ -185,15 +42,53 @@ static void fuse_lookup_init(struct fuse_req *req, struct inode *dir,
        req->out.args[0].value = outarg;
 }
 
-static inline unsigned long time_to_jiffies(unsigned long sec,
-                                           unsigned long nsec)
+static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
 {
-       /* prevent wrapping of jiffies */
-       if (sec + 1 >= LONG_MAX / HZ)
+       if (!entry->d_inode || is_bad_inode(entry->d_inode))
                return 0;
+       else if (entry->d_time && time_after(jiffies, entry->d_time)) {
+               int err;
+               int version;
+               struct fuse_entry_out outarg;
+               struct inode *inode = entry->d_inode;
+               struct fuse_inode *fi = get_fuse_inode(inode);
+               struct fuse_conn *fc = get_fuse_conn(inode);
+               struct fuse_req *req = fuse_get_request_nonint(fc);
+               if (!req)
+                       return 0;
 
-       return jiffies + sec * HZ + nsec / (1000000000 / HZ);
+               fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg);
+               request_send_nonint(fc, req);
+               version = req->out.h.unique;
+               err = req->out.h.error;
+               fuse_put_request(fc, req);
+               if (err || outarg.nodeid != get_node_id(inode) ||
+                   (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
+                       return 0;
+
+               fuse_change_attributes(inode, &outarg.attr);
+               inode->i_version = version;
+               entry->d_time = time_to_jiffies(outarg.entry_valid,
+                                               outarg.entry_valid_nsec);
+               fi->i_time = time_to_jiffies(outarg.attr_valid,
+                                            outarg.attr_valid_nsec);
+       }
+       return 1;
+}
+#ifndef KERNEL_2_6
+static int fuse_dentry_revalidate_2_4(struct dentry *entry, int flags)
+{
+       return fuse_dentry_revalidate(entry, NULL);
 }
+#endif
+
+static struct dentry_operations fuse_dentry_operations = {
+#ifdef KERNEL_2_6
+       .d_revalidate   = fuse_dentry_revalidate,
+#else
+       .d_revalidate   = fuse_dentry_revalidate_2_4,
+#endif
+};
 
 static int fuse_lookup_iget(struct inode *dir, struct dentry *entry,
                            struct inode **inodep)
@@ -207,6 +102,7 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry,
 
        if (entry->d_name.len > FUSE_NAME_MAX)
                return -ENAMETOOLONG;
+
        req = fuse_get_request(fc);
        if (!req)
                return -ERESTARTNOINTR;
@@ -262,6 +158,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
        int err;
 
        req->in.h.nodeid = get_node_id(dir);
+       req->inode = dir;
        req->out.numargs = 1;
        req->out.args[0].size = sizeof(outarg);
        req->out.args[0].value = &outarg;
@@ -377,6 +274,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
 
        req->in.h.opcode = FUSE_UNLINK;
        req->in.h.nodeid = get_node_id(dir);
+       req->inode = dir;
        req->in.numargs = 1;
        req->in.args[0].size = entry->d_name.len + 1;
        req->in.args[0].value = entry->d_name.name;
@@ -407,6 +305,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
 
        req->in.h.opcode = FUSE_RMDIR;
        req->in.h.nodeid = get_node_id(dir);
+       req->inode = dir;
        req->in.numargs = 1;
        req->in.args[0].size = entry->d_name.len + 1;
        req->in.args[0].value = entry->d_name.name;
@@ -435,6 +334,8 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
        inarg.newdir = get_node_id(newdir);
        req->in.h.opcode = FUSE_RENAME;
        req->in.h.nodeid = get_node_id(olddir);
+       req->inode = olddir;
+       req->inode2 = newdir;
        req->in.numargs = 3;
        req->in.args[0].size = sizeof(inarg);
        req->in.args[0].value = &inarg;
@@ -454,8 +355,7 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
                   rename actually took place.  If the invalidation
                   fails (e.g. some process has CWD under the renamed
                   directory), then there can be inconsistency between
-                  the dcache and the real filesystem.  But not a lot
-                  can be done about that */
+                  the dcache and the real filesystem.  Tough luck. */
                fuse_invalidate_entry(oldent);
                if (newent->d_inode)
                        fuse_invalidate_entry(newent);
@@ -478,6 +378,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
        memset(&inarg, 0, sizeof(inarg));
        inarg.newdir = get_node_id(newdir);
        req->in.h.opcode = FUSE_LINK;
+       req->inode2 = newdir;
        req->in.numargs = 2;
        req->in.args[0].size = sizeof(inarg);
        req->in.args[0].value = &inarg;
@@ -506,6 +407,7 @@ int fuse_do_getattr(struct inode *inode)
 
        req->in.h.opcode = FUSE_GETATTR;
        req->in.h.nodeid = get_node_id(inode);
+       req->inode = inode;
        req->out.numargs = 1;
        req->out.args[0].size = sizeof(arg);
        req->out.args[0].value = &arg;
@@ -513,10 +415,15 @@ int fuse_do_getattr(struct inode *inode)
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (!err) {
-               struct fuse_inode *fi = get_fuse_inode(inode);
-               change_attributes(inode, &arg.attr);
-               fi->i_time = time_to_jiffies(arg.attr_valid,
-                                            arg.attr_valid_nsec);
+               if ((inode->i_mode ^ arg.attr.mode) & S_IFMT) {
+                       make_bad_inode(inode);
+                       err = -EIO;
+               } else {
+                       struct fuse_inode *fi = get_fuse_inode(inode);
+                       fuse_change_attributes(inode, &arg.attr);
+                       fi->i_time = time_to_jiffies(arg.attr_valid,
+                                                    arg.attr_valid_nsec);
+               }
        }
        return err;
 }
@@ -594,7 +501,7 @@ static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
                struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
                size_t reclen = FUSE_DIRENT_SIZE(dirent);
                int over;
-               if (dirent->namelen > NAME_MAX)
+               if (dirent->namelen > FUSE_NAME_MAX)
                        return -EIO;
                if (reclen > nbytes)
                        break;
@@ -640,6 +547,7 @@ static int fuse_getdir(struct file *file)
 
        req->in.h.opcode = FUSE_GETDIR;
        req->in.h.nodeid = get_node_id(inode);
+       req->inode = inode;
        req->out.numargs = 1;
        req->out.args[0].size = sizeof(struct fuse_getdir_out);
        req->out.args[0].value = &outarg;
@@ -694,6 +602,7 @@ static char *read_link(struct dentry *dentry)
        }
        req->in.h.opcode = FUSE_READLINK;
        req->in.h.nodeid = get_node_id(inode);
+       req->inode = inode;
        req->out.argvar = 1;
        req->out.numargs = 1;
        req->out.args[0].size = PAGE_SIZE - 1;
@@ -823,6 +732,7 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
        inarg.valid = iattr_to_fattr(attr, &inarg.attr);
        req->in.h.opcode = FUSE_SETATTR;
        req->in.h.nodeid = get_node_id(inode);
+       req->inode = inode;
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(inarg);
        req->in.args[0].value = &inarg;
@@ -833,57 +743,26 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (!err) {
-               if (is_truncate) {
-                       loff_t origsize = i_size_read(inode);
-                       i_size_write(inode, outarg.attr.size);
-                       if (origsize > outarg.attr.size)
-                               vmtruncate(inode, outarg.attr.size);
+               if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
+                       make_bad_inode(inode);
+                       err = -EIO;
+               } else {
+                       if (is_truncate) {
+                               loff_t origsize = i_size_read(inode);
+                               i_size_write(inode, outarg.attr.size);
+                               if (origsize > outarg.attr.size)
+                                       vmtruncate(inode, outarg.attr.size);
+                       }
+                       fuse_change_attributes(inode, &outarg.attr);
+                       fi->i_time = time_to_jiffies(outarg.attr_valid,
+                                                    outarg.attr_valid_nsec);
                }
-               change_attributes(inode, &outarg.attr);
-               fi->i_time = time_to_jiffies(outarg.attr_valid,
-                                            outarg.attr_valid_nsec);
        } else if (err == -EINTR)
                fuse_invalidate_attr(inode);
 
        return err;
 }
 
-static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
-{
-       if (!entry->d_inode)
-               return 0;
-       else if (entry->d_time && time_after(jiffies, entry->d_time)) {
-               int err;
-               int version;
-               struct fuse_entry_out outarg;
-               struct inode *inode = entry->d_inode;
-               struct fuse_inode *fi = get_fuse_inode(inode);
-               struct fuse_conn *fc = get_fuse_conn(inode);
-               struct fuse_req *req = fuse_get_request_nonint(fc);
-               if (!req)
-                       return 0;
-
-               fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg);
-               request_send_nonint(fc, req, 0);
-               version = req->out.h.unique;
-               err = req->out.h.error;
-               fuse_put_request(fc, req);
-               if (err)
-                       return 0;
-
-               if (outarg.nodeid != get_node_id(inode))
-                       return 0;
-
-               change_attributes(inode, &outarg.attr);
-               inode->i_version = version;
-               entry->d_time = time_to_jiffies(outarg.entry_valid,
-                                               outarg.entry_valid_nsec);
-               fi->i_time = time_to_jiffies(outarg.attr_valid,
-                                            outarg.attr_valid_nsec);
-       }
-       return 1;
-}
-
 #ifdef KERNEL_2_6
 static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
                        struct kstat *stat)
@@ -932,11 +811,6 @@ static int fuse_mknod_2_4(struct inode *dir, struct dentry *entry, int mode,
        return fuse_mknod(dir, entry, mode, rdev);
 }
 
-static int fuse_dentry_revalidate_2_4(struct dentry *entry, int flags)
-{
-       return fuse_dentry_revalidate(entry, NULL);
-}
-
 static int fuse_create_2_4(struct inode *dir, struct dentry *entry, int mode)
 {
        return fuse_create(dir, entry, mode, NULL);
@@ -978,6 +852,7 @@ static int fuse_setxattr(struct dentry *entry, const char *name,
        inarg.flags = flags;
        req->in.h.opcode = FUSE_SETXATTR;
        req->in.h.nodeid = get_node_id(inode);
+       req->inode = inode;
        req->in.numargs = 3;
        req->in.args[0].size = sizeof(inarg);
        req->in.args[0].value = &inarg;
@@ -1016,6 +891,7 @@ static ssize_t fuse_getxattr(struct dentry *entry, const char *name,
        inarg.size = size;
        req->in.h.opcode = FUSE_GETXATTR;
        req->in.h.nodeid = get_node_id(inode);
+       req->inode = inode;
        req->in.numargs = 2;
        req->in.args[0].size = sizeof(inarg);
        req->in.args[0].value = &inarg;
@@ -1065,6 +941,7 @@ static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
        inarg.size = size;
        req->in.h.opcode = FUSE_LISTXATTR;
        req->in.h.nodeid = get_node_id(inode);
+       req->inode = inode;
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(inarg);
        req->in.args[0].value = &inarg;
@@ -1108,6 +985,7 @@ static int fuse_removexattr(struct dentry *entry, const char *name)
 
        req->in.h.opcode = FUSE_REMOVEXATTR;
        req->in.h.nodeid = get_node_id(inode);
+       req->inode = inode;
        req->in.numargs = 1;
        req->in.args[0].size = strlen(name) + 1;
        req->in.args[0].value = name;
@@ -1157,7 +1035,7 @@ static struct file_operations fuse_dir_operations = {
        .release        = fuse_dir_release,
 };
 
-static struct inode_operations fuse_file_inode_operations = {
+static struct inode_operations fuse_common_inode_operations = {
        .setattr        = fuse_setattr,
 #ifdef KERNEL_2_6
        .permission     = fuse_permission,
@@ -1191,10 +1069,18 @@ static struct inode_operations fuse_symlink_inode_operations = {
 #endif
 };
 
-static struct dentry_operations fuse_dentry_operations = {
-#ifdef KERNEL_2_6
-       .d_revalidate   = fuse_dentry_revalidate,
-#else
-       .d_revalidate   = fuse_dentry_revalidate_2_4,
-#endif
-};
+void fuse_init_common(struct inode *inode)
+{
+       inode->i_op = &fuse_common_inode_operations;
+}
+
+void fuse_init_dir(struct inode *inode)
+{
+       inode->i_op = &fuse_dir_inode_operations;
+       inode->i_fop = &fuse_dir_operations;
+}
+
+void fuse_init_symlink(struct inode *inode)
+{
+       inode->i_op = &fuse_symlink_inode_operations;
+}
index aa0265156d657245917a61197b6c2c0bc6261653..b7359c5206c8e1c0a89996a097a6f8f4c9259120 100644 (file)
@@ -37,12 +37,9 @@ static int fuse_open(struct inode *inode, struct file *file)
                        return err;
        }
 
-       /* Prevent concurrent unlink or rename */
-       down(&inode->i_sem);
-       err = -ERESTARTSYS;
        req = fuse_get_request(fc);
        if (!req)
-               goto out;
+               return -ERESTARTSYS;
 
        err = -ENOMEM;
        ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL);
@@ -56,9 +53,10 @@ static int fuse_open(struct inode *inode, struct file *file)
        }
 
        memset(&inarg, 0, sizeof(inarg));
-       inarg.flags = file->f_flags & ~O_EXCL;
+       inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
        req->in.h.opcode = FUSE_OPEN;
        req->in.h.nodeid = get_node_id(inode);
+       req->inode = inode;
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(inarg);
        req->in.args[0].value = &inarg;
@@ -85,8 +83,6 @@ static int fuse_open(struct inode *inode, struct file *file)
 
  out_put_request:
        fuse_put_request(fc, req);
- out:
-       up(&inode->i_sem);
        return err;
 }
 
@@ -101,11 +97,11 @@ static int fuse_release(struct inode *inode, struct file *file)
        inarg->flags = file->f_flags & ~O_EXCL;
        req->in.h.opcode = FUSE_RELEASE;
        req->in.h.nodeid = get_node_id(inode);
+       req->inode = inode;
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(struct fuse_release_in);
        req->in.args[0].value = inarg;
-       request_send_nonint(fc, req, 1);
-       fuse_put_request(fc, req);
+       request_send_background(fc, req);
        kfree(ff);
 
        /* Return value is ignored by VFS */
@@ -132,10 +128,12 @@ static int fuse_flush(struct file *file)
        inarg.fh = ff->fh;
        req->in.h.opcode = FUSE_FLUSH;
        req->in.h.nodeid = get_node_id(inode);
+       req->inode = inode;
+       req->file = file;
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(inarg);
        req->in.args[0].value = &inarg;
-       request_send_nonint(fc, req, 0);
+       request_send_nonint(fc, req);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (err == -ENOSYS) {
@@ -163,9 +161,11 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.fh = ff->fh;
-       inarg.datasync = datasync;
+       inarg.fsync_flags = datasync ? 1 : 0;
        req->in.h.opcode = FUSE_FSYNC;
        req->in.h.nodeid = get_node_id(inode);
+       req->inode = inode;
+       req->file = file;
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(inarg);
        req->in.args[0].value = &inarg;
@@ -192,6 +192,8 @@ static ssize_t fuse_send_read(struct fuse_req *req, struct file *file,
        inarg.size = count;
        req->in.h.opcode = FUSE_READ;
        req->in.h.nodeid = get_node_id(inode);
+       req->inode = inode;
+       req->file = file;
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(struct fuse_read_in);
        req->in.args[0].value = &inarg;
@@ -199,19 +201,19 @@ static ssize_t fuse_send_read(struct fuse_req *req, struct file *file,
        req->out.argvar = 1;
        req->out.numargs = 1;
        req->out.args[0].size = count;
-       request_send_nonint(fc, req, 0);
+       request_send_nonint(fc, req);
        return req->out.args[0].size;
 }
 
 static int fuse_readpage(struct file *file, struct page *page)
 {
-       int err;
        struct inode *inode = page->mapping->host;
        struct fuse_conn *fc = get_fuse_conn(inode);
        loff_t pos = (loff_t) page->index << PAGE_CACHE_SHIFT;
        struct fuse_req *req = fuse_get_request_nonint(fc);
+       int err = -EINTR;
        if (!req)
-               return -EINTR;
+               goto out;
 
        req->out.page_zeroing = 1;
        req->num_pages = 1;
@@ -221,6 +223,7 @@ static int fuse_readpage(struct file *file, struct page *page)
        fuse_put_request(fc, req);
        if (!err)
                SetPageUptodate(page);
+ out:
        unlock_page(page);
        return err;
 }
@@ -261,9 +264,10 @@ static int fuse_readpages_fill(void *_data, struct page *page)
             (req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_read ||
             req->pages[req->num_pages - 1]->index + 1 != page->index)) {
                int err = fuse_send_readpages(req, data->file, inode);
-               if (err)
+               if (err) {
+                       unlock_page(page);
                        return err;
-
+               }
                fuse_reset_request(req);
        }
        req->pages[req->num_pages] = page;
@@ -278,7 +282,6 @@ static int fuse_readpages(struct file *file, struct address_space *mapping,
        struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_readpages_data data;
        int err;
-
        data.file = file;
        data.inode = inode;
        data.req = fuse_get_request_nonint(fc);
@@ -287,7 +290,7 @@ static int fuse_readpages(struct file *file, struct address_space *mapping,
 
        err = read_cache_pages(mapping, pages, fuse_readpages_fill, &data);
        if (!err && data.req->num_pages)
-               fuse_send_readpages(data.req, file, inode);
+               err = fuse_send_readpages(data.req, file, inode);
        fuse_put_request(fc, data.req);
        return err;
 }
@@ -400,12 +403,13 @@ static ssize_t fuse_send_write(struct fuse_req *req, struct file *file,
        struct fuse_write_out outarg;
 
        memset(&inarg, 0, sizeof(struct fuse_write_in));
-       inarg.writepage = 0;
        inarg.fh = ff->fh;
        inarg.offset = pos;
        inarg.size = count;
        req->in.h.opcode = FUSE_WRITE;
        req->in.h.nodeid = get_node_id(inode);
+       req->inode = inode;
+       req->file = file;
        req->in.argpages = 1;
        req->in.numargs = 2;
        req->in.args[0].size = sizeof(struct fuse_write_in);
@@ -414,7 +418,7 @@ static ssize_t fuse_send_write(struct fuse_req *req, struct file *file,
        req->out.numargs = 1;
        req->out.args[0].size = sizeof(struct fuse_write_out);
        req->out.args[0].value = &outarg;
-       request_send_nonint(fc, req, 0);
+       request_send_nonint(fc, req);
        return outarg.size;
 }
 
@@ -584,6 +588,7 @@ static ssize_t fuse_file_write(struct file *file, const char __user *buf,
 
        if (fc->flags & FUSE_DIRECT_IO) {
                ssize_t res;
+               /* Don't allow parallel writes to the same file */
                down(&inode->i_sem);
                res = fuse_direct_io(file, buf, count, ppos, 1);
                up(&inode->i_sem);
index a5660b1dc0792529599b2057e6a8852900ba669a..9812f31f36f0e57a43e322191a59f4c7a9ff1f70 100644 (file)
@@ -37,6 +37,8 @@
 #     define i_size_read(inode) ((inode)->i_size)
 #     define i_size_write(inode, size) do { (inode)->i_size = size; } while(0)
 #  endif
+#  define new_decode_dev(x) (x)
+#  define new_encode_dev(x) (x)
 #endif /* KERNEL_2_6 */
 #endif /* FUSE_MAINLINE */
 #include <linux/fs.h>
@@ -64,10 +66,10 @@ static inline void set_page_dirty_lock(struct page *page)
        unlock_page(page);
 }
 #endif
-/* Max number of pages that can be used in a single read request */
+/** Max number of pages that can be used in a single read request */
 #define FUSE_MAX_PAGES_PER_REQ 32
 
-/* If more requests are outstanding, then the operation will block */
+/** If more requests are outstanding, then the operation will block */
 #define FUSE_MAX_OUTSTANDING 10
 
 /** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem
@@ -99,7 +101,7 @@ static inline void set_page_dirty_lock(struct page *page)
 struct fuse_inode {
        /** Unique ID, which identifies the inode between userspace
         * and kernel */
-       unsigned long nodeid;
+       u64 nodeid;
 
        /** The request used for sending the FORGET message */
        struct fuse_req *forget_req;
@@ -114,7 +116,7 @@ struct fuse_file {
        struct fuse_req *release_req;
 
        /** File handle used by userspace */
-       unsigned long fh;
+       u64 fh;
 };
 
 /** One input argument of a request */
@@ -189,6 +191,9 @@ struct fuse_req {
        /** The request was interrupted */
        unsigned interrupted:1;
 
+       /** Request is sent in the background */
+       unsigned background:1;
+       
        /** Data is being copied to/from the request */
        unsigned locked:1;
 
@@ -221,6 +226,15 @@ struct fuse_req {
 
        /** offset of data on first page */
        unsigned page_offset;
+
+       /** Inode used in the request */
+       struct inode *inode;
+       
+       /** Second inode used in the request (or NULL) */
+       struct inode *inode2;
+
+       /** File used in the request (or NULL) */
+       struct file *file;
 };
 
 /**
@@ -259,7 +273,11 @@ struct fuse_conn {
        struct list_head processing;
 
        /** Controls the maximum number of outstanding requests */
-       struct semaphore unused_sem;
+       struct semaphore outstanding_sem;
+
+       /** This counts the number of outstanding requests if
+           outstanding_sem would go negative */
+       unsigned outstanding_debt;
 
        /** The list of unused requests */
        struct list_head unused_list;
@@ -320,7 +338,7 @@ static inline struct fuse_inode *get_fuse_inode(struct inode *inode)
        return (struct fuse_inode *) (&inode[1]);
 }
 
-static inline unsigned long get_node_id(struct inode *inode)
+static inline u64 get_node_id(struct inode *inode)
 {
        return get_fuse_inode(inode)->nodeid;
 }
@@ -348,15 +366,6 @@ extern spinlock_t fuse_lock;
 struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
                        int generation, struct fuse_attr *attr, int version);
 
-/**
- * Lookup an inode by nodeid
- */
-#ifdef KERNEL_2_6
-struct inode *fuse_ilookup(struct super_block *sb, unsigned long nodeid);
-#else
-struct inode *fuse_ilookup(struct super_block *sb, ino_t ino, unsigned long nodeid);
-#endif
-
 /**
  * Send FORGET command
  */
@@ -364,10 +373,30 @@ void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
                      unsigned long nodeid, int version);
 
 /**
- * Initialise operations on regular file
+ * Initialise file operations on a regular file
  */
 void fuse_init_file_inode(struct inode *inode);
 
+/**
+ * Initialise inode operations on regular files and special files
+ */
+void fuse_init_common(struct inode *inode);
+
+/**
+ * Initialise inode and file operations on a directory
+ */
+void fuse_init_dir(struct inode *inode);
+
+/**
+ * Initialise inode operations on a symlink
+ */
+void fuse_init_symlink(struct inode *inode);
+
+/**
+ * Change attributes of an inode
+ */
+void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr);
+
 /**
  * Check if the connection can be released, and if yes, then free the
  * connection structure
@@ -431,18 +460,19 @@ void request_send(struct fuse_conn *fc, struct fuse_req *req);
 
 /**
  * Send a request (synchronous, non-interruptible except by SIGKILL)
- *
- * If background is non-zero and SIGKILL is received still send
- * request asynchronously
  */
-void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req,
-                        int background);
+void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req);
 
 /**
  * Send a request with no reply
  */
 void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req);
 
+/**
+ * Send a request in the background
+ */
+void request_send_background(struct fuse_conn *fc, struct fuse_req *req);
+
 /**
  * Get the attributes of a file
  */
index a97c3e8c0896e96f0648d3c12cc92ddd21fec489..bad4546152c135cad9eb8655ab8b8097e3de5933 100644 (file)
@@ -8,6 +8,8 @@
 
 /* This file defines the kernel interface of FUSE */
 
+#include <asm/types.h>
+
 /** Version number of this interface */
 #define FUSE_KERNEL_VERSION 5
 
 #define FUSE_MINOR 229
 
 struct fuse_attr {
-       unsigned long       ino;
-       unsigned int        mode;
-       unsigned int        nlink;
-       unsigned int        uid;
-       unsigned int        gid;
-       unsigned int        rdev;
-       unsigned long long  size;
-       unsigned long       blocks;
-       unsigned long       atime;
-       unsigned long       atimensec;
-       unsigned long       mtime;
-       unsigned long       mtimensec;
-       unsigned long       ctime;
-       unsigned long       ctimensec;
+       __u64   ino;
+       __u64   size;
+       __u64   blocks;
+       __u64   atime;
+       __u64   mtime;
+       __u64   ctime;
+       __u32   atimensec;
+       __u32   mtimensec;
+       __u32   ctimensec;
+       __u32   mode;
+       __u32   nlink;
+       __u32   uid;
+       __u32   gid;
+       __u32   rdev;
 };
 
 struct fuse_kstatfs {
-       unsigned int        bsize;
-       unsigned long long  blocks;
-       unsigned long long  bfree;
-       unsigned long long  bavail;
-       unsigned long long  files;
-       unsigned long long  ffree;
-       unsigned int        namelen;
+       __u64   blocks;
+       __u64   bfree;
+       __u64   bavail;
+       __u64   files;
+       __u64   ffree;
+       __u32   bsize;
+       __u32   namelen;
 };
 
 #define FATTR_MODE     (1 << 0)
@@ -94,86 +96,87 @@ enum fuse_opcode {
 #define FUSE_XATTR_SIZE_MAX 4096
 
 struct fuse_entry_out {
-       unsigned long nodeid;      /* Inode ID */
-       unsigned long generation;  /* Inode generation: nodeid:gen must
-                                      be unique for the fs's lifetime */
-       unsigned long entry_valid; /* Cache timeout for the name */
-       unsigned long entry_valid_nsec;
-       unsigned long attr_valid;  /* Cache timeout for the attributes */
-       unsigned long attr_valid_nsec;
+       __u64   nodeid;         /* Inode ID */
+       __u64   generation;     /* Inode generation: nodeid:gen must
+                                  be unique for the fs's lifetime */
+       __u64   entry_valid;    /* Cache timeout for the name */
+       __u64   attr_valid;     /* Cache timeout for the attributes */
+       __u32   entry_valid_nsec;
+       __u32   attr_valid_nsec;
        struct fuse_attr attr;
 };
 
 struct fuse_forget_in {
-       int version;
+       __u64   version;
 };
 
 struct fuse_attr_out {
-       unsigned long attr_valid;  /* Cache timeout for the attributes */
-       unsigned long attr_valid_nsec;
+       __u64   attr_valid;     /* Cache timeout for the attributes */
+       __u32   attr_valid_nsec;
+       __u32   dummy;
        struct fuse_attr attr;
 };
 
 struct fuse_getdir_out {
-       int fd;
+       __u32   fd;
 };
 
 struct fuse_mknod_in {
-       unsigned int mode;
-       unsigned int rdev;
+       __u32   mode;
+       __u32   rdev;
 };
 
 struct fuse_mkdir_in {
-       unsigned int mode;
+       __u32   mode;
 };
 
 struct fuse_rename_in {
-       unsigned long newdir;
+       __u64   newdir;
 };
 
 struct fuse_link_in {
-       unsigned long newdir;
+       __u64   newdir;
 };
 
 struct fuse_setattr_in {
+       __u32   valid;
        struct fuse_attr attr;
-       unsigned int valid;
 };
 
 struct fuse_open_in {
-       unsigned int flags;
+       __u32   flags;
 };
 
 struct fuse_open_out {
-       unsigned long fh;
-       unsigned int _open_flags;
+       __u64   fh;
+       __u32   open_flags;
 };
 
 struct fuse_release_in {
-       unsigned long fh;
-       unsigned int flags;
+       __u64   fh;
+       __u32   flags;
 };
 
 struct fuse_flush_in {
-       unsigned long fh;
-       unsigned int _nref;
+       __u64   fh;
+       __u32   flush_flags;
 };
 
 struct fuse_read_in {
-       unsigned long fh;
-       unsigned long long offset;
-       unsigned int size;
+       __u64   fh;
+       __u64   offset;
+       __u32   size;
 };
 
 struct fuse_write_in {
-       int writepage;
-       unsigned long fh;
-       unsigned long long offset;
-       unsigned int size;
+       __u64   fh;
+       __u64   offset;
+       __u32   size;
+       __u32   write_flags;
 };
 
 struct fuse_write_out {
-       unsigned int size;
+       __u32   size;
 };
 
 struct fuse_statfs_out {
@@ -181,45 +184,47 @@ struct fuse_statfs_out {
 };
 
 struct fuse_fsync_in {
-       unsigned long fh;
-       int datasync;
+       __u64   fh;
+       __u32   fsync_flags;
 };
 
 struct fuse_setxattr_in {
-       unsigned int size;
-       unsigned int flags;
+       __u32   size;
+       __u32   flags;
 };
 
 struct fuse_getxattr_in {
-       unsigned int size;
+       __u32   size;
 };
 
 struct fuse_getxattr_out {
-       unsigned int size;
+       __u32   size;
 };
 
 struct fuse_in_header {
-       int unique;
-       enum fuse_opcode opcode;
-       unsigned long nodeid;
-       unsigned int uid;
-       unsigned int gid;
-       unsigned int pid;
+       __u32   len;
+       __u32   opcode;
+       __u64   unique;
+       __u64   nodeid;
+       __u32   uid;
+       __u32   gid;
+       __u32   pid;
 };
 
 struct fuse_out_header {
-       int unique;
-       int error;
+       __u32   len;
+       __s32   error;
+       __u64   unique;
 };
 
 struct fuse_dirent {
-       unsigned long ino;
-       unsigned short namelen;
-       unsigned char type;
-       char name[256];
+       __u64   ino;
+       __u32   namelen;
+       __u32   type;
+       char name[0];
 };
 
-#define FUSE_NAME_OFFSET ((unsigned int) ((struct fuse_dirent *) 0)->name)
-#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(long) - 1) & ~(sizeof(long) - 1))
+#define FUSE_NAME_OFFSET ((unsigned) ((struct fuse_dirent *) 0)->name)
+#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1))
 #define FUSE_DIRENT_SIZE(d) \
        FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
index ac80b632653be3ac19ab696e4c1f50bd920a50a0..eb40b7b7c2d8a1523517f3fc5e25d4bc1b4bf1c3 100644 (file)
 #endif
 
 static kmem_cache_t *fuse_inode_cachep;
+static int mount_count;
 
 static int user_allow_other;
+static int mount_max = 1000;
 #ifdef KERNEL_2_6
 module_param(user_allow_other, int, 0644);
+module_param(mount_max, int, 0644);
 #else
 MODULE_PARM(user_allow_other, "i");
+MODULE_PARM(mount_max, "i");
 #endif
 MODULE_PARM_DESC(user_allow_other, "Allow non root user to specify the \"allow_other\" or \"allow_root\" mount options");
+MODULE_PARM_DESC(mount_max, "Maximum number of FUSE mounts allowed, if -1 then unlimited (default: 1000)");
 
 #define FUSE_SUPER_MAGIC 0x65735546
 
@@ -112,11 +117,155 @@ static void fuse_clear_inode(struct inode *inode)
        }
 }
 
+void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
+{
+       if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size)
+#ifdef KERNEL_2_6
+               invalidate_inode_pages(inode->i_mapping);
+#else
+               invalidate_inode_pages(inode);
+#endif
+
+       inode->i_ino     = attr->ino;
+       inode->i_mode    = (inode->i_mode & S_IFMT) + (attr->mode & 07777);
+       inode->i_nlink   = attr->nlink;
+       inode->i_uid     = attr->uid;
+       inode->i_gid     = attr->gid;
+       i_size_write(inode, attr->size);
+       inode->i_blksize = PAGE_CACHE_SIZE;
+       inode->i_blocks  = attr->blocks;
+#ifdef KERNEL_2_6
+       inode->i_atime.tv_sec   = attr->atime;
+       inode->i_atime.tv_nsec  = attr->atimensec;
+       inode->i_mtime.tv_sec   = attr->mtime;
+       inode->i_mtime.tv_nsec  = attr->mtimensec;
+       inode->i_ctime.tv_sec   = attr->ctime;
+       inode->i_ctime.tv_nsec  = attr->ctimensec;
+#else
+       inode->i_atime   = attr->atime;
+       inode->i_mtime   = attr->mtime;
+       inode->i_ctime   = attr->ctime;
+#endif
+}
+
+static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
+{
+       inode->i_mode = attr->mode & S_IFMT;
+       i_size_write(inode, attr->size);
+       if (S_ISREG(inode->i_mode)) {
+               fuse_init_common(inode);
+               fuse_init_file_inode(inode);
+       } else if (S_ISDIR(inode->i_mode))
+               fuse_init_dir(inode);
+       else if (S_ISLNK(inode->i_mode))
+               fuse_init_symlink(inode);
+       else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
+                S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
+               fuse_init_common(inode);
+               init_special_inode(inode, inode->i_mode,
+                                  new_decode_dev(attr->rdev));
+       } else {
+               /* Don't let user create weird files */
+               inode->i_mode = S_IFREG;
+               fuse_init_common(inode);
+               fuse_init_file_inode(inode);
+       }
+}
+
+#ifdef KERNEL_2_6
+static int fuse_inode_eq(struct inode *inode, void *_nodeidp)
+{
+       unsigned long nodeid = *(unsigned long *) _nodeidp;
+       if (get_node_id(inode) == nodeid)
+               return 1;
+       else
+               return 0;
+}
+
+static int fuse_inode_set(struct inode *inode, void *_nodeidp)
+{
+       unsigned long nodeid = *(unsigned long *) _nodeidp;
+       get_fuse_inode(inode)->nodeid = nodeid;
+       return 0;
+}
+
+struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
+                       int generation, struct fuse_attr *attr, int version)
+{
+       struct inode *inode;
+       struct fuse_conn *fc = get_fuse_conn_super(sb);
+       int retried = 0;
+
+ retry:
+       inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid);
+       if (!inode)
+               return NULL;
+
+       if ((inode->i_state & I_NEW)) {
+               inode->i_generation = generation;
+               inode->i_data.backing_dev_info = &fc->bdi;
+               fuse_init_inode(inode, attr);
+               unlock_new_inode(inode);
+       } else if ((inode->i_mode ^ attr->mode) & S_IFMT) {
+               BUG_ON(retried);
+               /* Inode has changed type, any I/O on the old should fail */
+               make_bad_inode(inode);
+               iput(inode);
+               retried = 1;
+               goto retry;
+       }
+
+       fuse_change_attributes(inode, attr);
+       inode->i_version = version;
+       return inode;
+}
+#else
+static int fuse_inode_eq(struct inode *inode, unsigned long ino, void *_nodeidp){
+       unsigned long nodeid = *(unsigned long *) _nodeidp;
+       if (inode->u.generic_ip && get_node_id(inode) == nodeid)
+               return 1;
+       else
+               return 0;
+}
+
+struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
+                       int generation, struct fuse_attr *attr, int version)
+{
+       struct inode *inode;
+       int retried = 0;
+
+ retry:
+       inode = iget4(sb, attr->ino, fuse_inode_eq, &nodeid);
+       if (!inode)
+               return NULL;
+
+       if (!inode->u.generic_ip) {
+               get_fuse_inode(inode)->nodeid = nodeid;
+               inode->u.generic_ip = inode;
+               inode->i_generation = generation;
+               fuse_init_inode(inode, attr);
+       } else if ((inode->i_mode ^ attr->mode) & S_IFMT) {
+               BUG_ON(retried);
+               /* Inode has changed type, any I/O on the old should fail */
+               remove_inode_hash(inode);
+               make_bad_inode(inode);
+               iput(inode);
+               retried = 1;
+               goto retry;
+       }
+
+       fuse_change_attributes(inode, attr);
+       inode->i_version = version;
+       return inode;
+}
+#endif
+
 static void fuse_put_super(struct super_block *sb)
 {
        struct fuse_conn *fc = get_fuse_conn_super(sb);
 
        spin_lock(&fuse_lock);
+       mount_count --;
        fc->sb = NULL;
        fc->uid = 0;
        fc->flags = 0;
@@ -329,7 +478,7 @@ static struct fuse_conn *new_conn(void)
                INIT_LIST_HEAD(&fc->pending);
                INIT_LIST_HEAD(&fc->processing);
                INIT_LIST_HEAD(&fc->unused_list);
-               sema_init(&fc->unused_sem, FUSE_MAX_OUTSTANDING);
+               sema_init(&fc->outstanding_sem, FUSE_MAX_OUTSTANDING);
                for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) {
                        struct fuse_req *req = fuse_request_alloc();
                        if (!req) {
@@ -392,7 +541,7 @@ static struct dentry *fuse_get_dentry(struct super_block *sb, void *vobjp)
        if (nodeid == 0)
                return ERR_PTR(-ESTALE);
 
-       inode = fuse_ilookup(sb, nodeid);
+       inode = ilookup5(sb, nodeid, fuse_inode_eq, &nodeid);
        if (!inode || inode->i_generation != generation)
                return ERR_PTR(-ESTALE);
 
@@ -449,12 +598,24 @@ static struct super_operations fuse_super_operations = {
        .show_options   = fuse_show_options,
 };
 
+static int inc_mount_count(void)
+{
+       int success = 0;
+       spin_lock(&fuse_lock);
+       mount_count ++;
+       if (mount_max == -1 || mount_count <= mount_max)
+               success = 1;
+       spin_unlock(&fuse_lock);
+       return success;
+}
+
 static int fuse_read_super(struct super_block *sb, void *data, int silent)
 {
        struct fuse_conn *fc;
        struct inode *root;
        struct fuse_mount_data d;
        struct file *file;
+       int err;
 
        if (!parse_fuse_opt((char *) data, &d))
                return -EINVAL;
@@ -493,6 +654,11 @@ static int fuse_read_super(struct super_block *sb, void *data, int silent)
 
        *get_fuse_conn_super_p(sb) = fc;
 
+       err = -ENFILE;
+       if (!inc_mount_count() && current->uid != 0)
+               goto err;
+
+       err = -ENOMEM;
        root = get_root_inode(sb, d.rootmode);
        if (root == NULL)
                goto err;
@@ -507,11 +673,12 @@ static int fuse_read_super(struct super_block *sb, void *data, int silent)
 
  err:
        spin_lock(&fuse_lock);
+       mount_count --;
        fc->sb = NULL;
        fuse_release_conn(fc);
        spin_unlock(&fuse_lock);
        *get_fuse_conn_super_p(sb) = NULL;
-       return -EINVAL;
+       return err;
 }
 
 #ifdef KERNEL_2_6
index 1e120e8752f459dc8f01ce4446f1a46f889b47ab..b5be472384b65db03a256f77b8e5ced60da83d89 100644 (file)
@@ -8,15 +8,31 @@
 
 #include <config.h>
 #include "fuse_i.h"
+#include "fuse_compat.h"
 #include "fuse_kernel.h"
 
+#include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <limits.h>
 #include <errno.h>
+#include <assert.h>
+#include <stdint.h>
 #include <sys/param.h>
 
+/* FUSE flags: */
+
+/** Enable debuging output */
+#define FUSE_DEBUG       (1 << 1)
+
+/** If a file is removed but it's still open, don't hide the file but
+    remove it immediately */
+#define FUSE_HARD_REMOVE (1 << 2)
+
+/** Use st_ino field in getattr instead of generating inode numbers  */
+#define FUSE_USE_INO     (1 << 3)
+
 #define FUSE_KERNEL_MINOR_VERSION_NEED 1
 #define FUSE_VERSION_FILE_OLD "/proc/fs/fuse/version"
 #define FUSE_VERSION_FILE_NEW "/sys/fs/fuse/version"
 #define ENTRY_REVALIDATE_TIME 1 /* sec */
 #define ATTR_REVALIDATE_TIME 1 /* sec */
 
+
+struct node {
+    struct node *name_next;
+    struct node *id_next;
+    nodeid_t nodeid;
+    unsigned int generation;
+    int refctr;
+    nodeid_t parent;
+    char *name;
+    uint64_t version;
+    int open_count;
+    int is_hidden;
+};
+
+struct fuse_dirhandle {
+    struct fuse *fuse;
+    nodeid_t dir;
+    FILE *fp;
+};
+
+struct fuse_cmd {
+    char *buf;
+    size_t buflen;
+};
+
+
 static struct fuse_context *(*fuse_getcontext)(void) = NULL;
 
 static const char *opname(enum fuse_opcode opcode)
@@ -61,11 +103,18 @@ static const char *opname(enum fuse_opcode opcode)
     }
 }
 
-static inline void dec_avail(struct fuse *f)
+static inline void fuse_dec_avail(struct fuse *f)
 {
-    pthread_mutex_lock(&f->lock);
+    pthread_mutex_lock(&f->worker_lock);
     f->numavail --;
-    pthread_mutex_unlock(&f->lock);
+    pthread_mutex_unlock(&f->worker_lock);
+}
+
+static inline void fuse_inc_avail(struct fuse *f)
+{
+    pthread_mutex_lock(&f->worker_lock);
+    f->numavail ++;
+    pthread_mutex_unlock(&f->worker_lock);
 }
 
 static struct node *get_node_nocheck(struct fuse *f, nodeid_t nodeid)
@@ -83,18 +132,18 @@ static struct node *get_node_nocheck(struct fuse *f, nodeid_t nodeid)
 static struct node *get_node(struct fuse *f, nodeid_t nodeid)
 {
     struct node *node = get_node_nocheck(f, nodeid);
-    if (node != NULL)
-        return node;
-    
-    fprintf(stderr, "fuse internal error: inode %lu not found\n", nodeid);
-    abort();
+    if (!node) {
+        fprintf(stderr, "fuse internal error: node %lu not found\n",
+                nodeid);
+        abort();
+    }
+    return node;
 }
 
-static void hash_id(struct fuse *f, struct node *node)
+static void free_node(struct node *node)
 {
-    size_t hash = node->nodeid % f->id_table_size;
-    node->id_next = f->id_table[hash];
-    f->id_table[hash] = node;    
+    free(node->name);
+    free(node);
 }
 
 static void unhash_id(struct fuse *f, struct node *node)
@@ -109,20 +158,11 @@ static void unhash_id(struct fuse *f, struct node *node)
         }
 }
 
-static nodeid_t next_id(struct fuse *f)
-{
-    do {
-        f->ctr++;
-        if (!f->ctr)
-            f->generation ++;
-    } while (f->ctr == 0 || get_node_nocheck(f, f->ctr) != NULL);
-    return f->ctr;
-}
-
-static void free_node(struct node *node)
+static void hash_id(struct fuse *f, struct node *node)
 {
-    free(node->name);
-    free(node);
+    size_t hash = node->nodeid % f->id_table_size;
+    node->id_next = f->id_table[hash];
+    f->id_table[hash] = node;    
 }
 
 static unsigned int name_hash(struct fuse *f, nodeid_t parent, const char *name)
@@ -136,36 +176,11 @@ static unsigned int name_hash(struct fuse *f, nodeid_t parent, const char *name)
     return (hash + parent) % f->name_table_size;
 }
 
-static struct node *lookup_node(struct fuse *f, nodeid_t parent,
-                                const char *name)
-{
-    size_t hash = name_hash(f, parent, name);
-    struct node *node;
-
-    for (node = f->name_table[hash]; node != NULL; node = node->name_next)
-        if (node->parent == parent && strcmp(node->name, name) == 0)
-            return node;
-
-    return NULL;
-}
-
-static int hash_name(struct fuse *f, struct node *node, nodeid_t parent,
-                     const char *name)
-{
-    size_t hash = name_hash(f, parent, name);
-    node->parent = parent;
-    node->name = strdup(name);
-    if (node->name == NULL)
-        return -1;
-
-    node->name_next = f->name_table[hash];
-    f->name_table[hash] = node;
-    return 0;
-}
+static void unref_node(struct fuse *f, struct node *node);
 
 static void unhash_name(struct fuse *f, struct node *node)
 {
-    if (node->name != NULL) {
+    if (node->name) {
         size_t hash = name_hash(f, node->parent, node->name);
         struct node **nodep = &f->name_table[hash];
         
@@ -173,6 +188,7 @@ static void unhash_name(struct fuse *f, struct node *node)
             if (*nodep == node) {
                 *nodep = node->name_next;
                 node->name_next = NULL;
+                unref_node(f, get_node(f, node->parent));
                 free(node->name);
                 node->name = NULL;
                 node->parent = 0;
@@ -184,8 +200,61 @@ static void unhash_name(struct fuse *f, struct node *node)
     }
 }
 
+static int hash_name(struct fuse *f, struct node *node, nodeid_t parent,
+                     const char *name)
+{
+    size_t hash = name_hash(f, parent, name);
+    node->name = strdup(name);
+    if (node->name == NULL)
+        return -1;
+
+    get_node(f, parent)->refctr ++;
+    node->parent = parent;
+    node->name_next = f->name_table[hash];
+    f->name_table[hash] = node;
+    return 0;
+}
+
+static void delete_node(struct fuse *f, struct node *node)
+{
+    assert(!node->name);
+    unhash_id(f, node);
+    free_node(node);
+}
+
+static void unref_node(struct fuse *f, struct node *node)
+{
+    assert(node->refctr > 0);
+    node->refctr --;
+    if (!node->refctr)
+        delete_node(f, node);
+}
+
+static nodeid_t next_id(struct fuse *f)
+{
+    do {
+        f->ctr++;
+        if (!f->ctr)
+            f->generation ++;
+    } while (f->ctr == 0 || get_node_nocheck(f, f->ctr) != NULL);
+    return f->ctr;
+}
+
+static struct node *lookup_node(struct fuse *f, nodeid_t parent,
+                                const char *name)
+{
+    size_t hash = name_hash(f, parent, name);
+    struct node *node;
+
+    for (node = f->name_table[hash]; node != NULL; node = node->name_next)
+        if (node->parent == parent && strcmp(node->name, name) == 0)
+            return node;
+
+    return NULL;
+}
+
 static struct node *find_node(struct fuse *f, nodeid_t parent, char *name,
-                              struct fuse_attr *attr, int version)
+                              struct fuse_attr *attr, uint64_t version)
 {
     struct node *node;
     int mode = attr->mode & S_IFMT;
@@ -197,38 +266,27 @@ static struct node *find_node(struct fuse *f, nodeid_t parent, char *name,
     pthread_mutex_lock(&f->lock);
     node = lookup_node(f, parent, name);
     if (node != NULL) {
-        if (node->mode == mode && node->rdev == rdev && 
-            (!(f->flags & FUSE_USE_INO) || node->ino == attr->ino)) {
-            if (!(f->flags & FUSE_USE_INO))
-                attr->ino = node->nodeid;
-
-            goto out;
-        }
+        if (!(f->flags & FUSE_USE_INO))
+            attr->ino = node->nodeid;        
+    } else {
+        node = (struct node *) calloc(1, sizeof(struct node));
+        if (node == NULL)
+            goto out_err;
         
-        unhash_name(f, node);
+        node->refctr = 1;
+        node->nodeid = next_id(f);
+        if (!(f->flags & FUSE_USE_INO))
+            attr->ino = node->nodeid;
+        node->open_count = 0;
+        node->is_hidden = 0;
+        node->generation = f->generation;
+        if (hash_name(f, node, parent, name) == -1) {
+            free(node);
+            node = NULL;
+            goto out_err;
+        }
+        hash_id(f, node);
     }
-
-    node = (struct node *) calloc(1, sizeof(struct node));
-    if (node == NULL)
-        goto out_err;
-
-    node->nodeid = next_id(f);
-    if (!(f->flags & FUSE_USE_INO))
-        attr->ino = node->nodeid;
-    node->mode = mode;
-    node->rdev = rdev;
-    node->ino = attr->ino;
-    node->open_count = 0;
-    node->is_hidden = 0;
-    node->generation = f->generation;
-    if (hash_name(f, node, parent, name) == -1) {
-        free(node);
-        node = NULL;
-        goto out_err;
-    }
-    hash_id(f, node);
-
- out:
     node->version = version;
  out_err:
     pthread_mutex_unlock(&f->lock);
@@ -265,8 +323,8 @@ static char *get_path_name(struct fuse *f, nodeid_t nodeid, const char *name)
     }
 
     pthread_mutex_lock(&f->lock);
-    for (node = get_node(f, nodeid); node->nodeid != FUSE_ROOT_ID;
-        node = get_node(f, node->parent)) {
+    for (node = get_node(f, nodeid); node && node->nodeid != FUSE_ROOT_ID;
+         node = get_node(f, node->parent)) {
         if (node->name == NULL) {
             s = NULL;
             break;
@@ -278,7 +336,7 @@ static char *get_path_name(struct fuse *f, nodeid_t nodeid, const char *name)
     }
     pthread_mutex_unlock(&f->lock);
 
-    if (s == NULL) 
+    if (node == NULL || s == NULL)
         return NULL;
     else if (*s == '\0')
         return strdup("/");
@@ -291,16 +349,16 @@ static char *get_path(struct fuse *f, nodeid_t nodeid)
     return get_path_name(f, nodeid, NULL);
 }
 
-static void destroy_node(struct fuse *f, nodeid_t nodeid, int version)
+static void forget_node(struct fuse *f, nodeid_t nodeid, uint64_t version)
 {
     struct node *node;
 
     pthread_mutex_lock(&f->lock);
-    node = get_node_nocheck(f, nodeid);
-    if (node && node->version == version && nodeid != FUSE_ROOT_ID) {
+    node = get_node(f, nodeid);
+    if (node->version == version && nodeid != FUSE_ROOT_ID) {
+        node->version = 0;
         unhash_name(f, node);
-        unhash_id(f, node);
-        free_node(node);
+        unref_node(f, node);
     }
     pthread_mutex_unlock(&f->lock);
 
@@ -312,12 +370,8 @@ static void remove_node(struct fuse *f, nodeid_t dir, const char *name)
 
     pthread_mutex_lock(&f->lock);
     node = lookup_node(f, dir, name);
-    if (node == NULL) {
-        fprintf(stderr, "fuse internal error: unable to remove node %lu/%s\n",
-                dir, name);
-        abort();
-    }
-    unhash_name(f, node);
+    if (node != NULL)
+        unhash_name(f, node);
     pthread_mutex_unlock(&f->lock);
 }
 
@@ -331,11 +385,8 @@ static int rename_node(struct fuse *f, nodeid_t olddir, const char *oldname,
     pthread_mutex_lock(&f->lock);
     node  = lookup_node(f, olddir, oldname);
     newnode  = lookup_node(f, newdir, newname);
-    if (node == NULL) {
-        fprintf(stderr, "fuse internal error: unable to rename node %lu/%s\n",
-                olddir, oldname);
-        abort();
-    }
+    if (node == NULL)
+        goto out;
 
     if (newnode != NULL) {
         if (hide) {
@@ -383,19 +434,28 @@ static void convert_stat(struct stat *stbuf, struct fuse_attr *attr)
 static int fill_dir(struct fuse_dirhandle *dh, const char *name, int type,
                     ino_t ino)
 {
-    struct fuse_dirent dirent;
+    size_t namelen = strlen(name);
+    struct fuse_dirent *dirent;
     size_t reclen;
     size_t res;
 
+    if (namelen > FUSE_NAME_MAX)
+        namelen = FUSE_NAME_MAX;
+
+    dirent = calloc(1, sizeof(struct fuse_dirent) + namelen + 8);
+    if (dirent == NULL)
+        return -ENOMEM;
+
     if ((dh->fuse->flags & FUSE_USE_INO))
-        dirent.ino = ino;
+        dirent->ino = ino;
     else
-        dirent.ino = (unsigned long) -1;
-    dirent.namelen = strlen(name);
-    strncpy(dirent.name, name, sizeof(dirent.name));
-    dirent.type = type;
-    reclen = FUSE_DIRENT_SIZE(&dirent);
-    res = fwrite(&dirent, reclen, 1, dh->fp);
+        dirent->ino = (unsigned long) -1;
+    dirent->namelen = namelen;
+    strncpy(dirent->name, name, namelen);
+    dirent->type = type;
+    reclen = FUSE_DIRENT_SIZE(dirent);
+    res = fwrite(dirent, reclen, 1, dh->fp);
+    free(dirent);
     if (res == 0) {
         perror("fuse: writing directory file");
         return -EIO;
@@ -403,26 +463,22 @@ static int fill_dir(struct fuse_dirhandle *dh, const char *name, int type,
     return 0;
 }
 
-static int send_reply_raw(struct fuse *f, char *outbuf, size_t outsize,
-                          int locked)
+static int send_reply_raw(struct fuse *f, char *outbuf, size_t outsize)
 {
     int res;
+    struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
+    out->len = outsize;
 
     if ((f->flags & FUSE_DEBUG)) {
-        struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
-        printf("   unique: %i, error: %i (%s), outsize: %i\n", out->unique,
-               out->error, strerror(-out->error), outsize);
+        printf("   unique: %llu, error: %i (%s), outsize: %i\n",
+               out->unique, out->error, strerror(-out->error), outsize);
         fflush(stdout);
     }
-
+    
     /* This needs to be done before the reply, otherwise the scheduler
-    could play tricks with us, and only let the counter be increased
-    long after the operation is done */
-    if (!locked)
-        pthread_mutex_lock(&f->lock);
-    f->numavail ++;
-    if (!locked)
-        pthread_mutex_unlock(&f->lock);
+       could play tricks with us, and only let the counter be
+       increased long after the operation is done */
+    fuse_inc_avail(f);
 
     res = write(f->fd, outbuf, outsize);
     if (res == -1) {
@@ -434,8 +490,8 @@ static int send_reply_raw(struct fuse *f, char *outbuf, size_t outsize,
     return 0;
 }
 
-static int do_send_reply(struct fuse *f, struct fuse_in_header *in, int error,
-                         void *arg, size_t argsize, int locked)
+static int send_reply(struct fuse *f, struct fuse_in_header *in, int error,
+                      void *arg, size_t argsize)
 {
     int res;
     char *outbuf;
@@ -463,25 +519,13 @@ static int do_send_reply(struct fuse *f, struct fuse_in_header *in, int error,
         if (argsize != 0)
             memcpy(outbuf + sizeof(struct fuse_out_header), arg, argsize);
         
-        res = send_reply_raw(f, outbuf, outsize, locked);
+        res = send_reply_raw(f, outbuf, outsize);
         free(outbuf);
     }
 
     return res;
 }
 
-static int send_reply(struct fuse *f, struct fuse_in_header *in, int error,
-                      void *arg, size_t argsize)
-{
-    return do_send_reply(f, in, error, arg, argsize, 0);
-}
-
-static int send_reply_locked(struct fuse *f, struct fuse_in_header *in,
-                             int error, void *arg, size_t argsize)
-{
-    return do_send_reply(f, in, error, arg, argsize, 1);
-}
-
 static int is_open(struct fuse *f, nodeid_t dir, const char *name)
 {
     struct node *node;
@@ -511,9 +555,8 @@ static char *hidden_name(struct fuse *f, nodeid_t dir, const char *oldname,
         pthread_mutex_lock(&f->lock);
         node = lookup_node(f, dir, oldname);
         if (node == NULL) {
-            fprintf(stderr, "fuse internal error: node %lu/%s not found\n",
-                    dir, oldname);
-            abort();
+            pthread_mutex_unlock(&f->lock);
+            return NULL;
         }
         do {
             f->hidectr ++;
@@ -556,8 +599,9 @@ static int hide_node(struct fuse *f, const char *oldpath, nodeid_t dir,
     return err;
 }
 
-static int lookup_path(struct fuse *f, nodeid_t nodeid, int version, char *name,
-                       const char *path, struct fuse_entry_out *arg)
+static int lookup_path(struct fuse *f, nodeid_t nodeid, uint64_t version,
+                       char *name, const char *path,
+                       struct fuse_entry_out *arg)
 {
     int res;
     struct stat buf;
@@ -579,7 +623,7 @@ static int lookup_path(struct fuse *f, nodeid_t nodeid, int version, char *name,
             arg->attr_valid = ATTR_REVALIDATE_TIME;
             arg->attr_valid_nsec = 0;
             if (f->flags & FUSE_DEBUG) {
-                printf("   NODEID: %li\n", arg->nodeid);
+                printf("   NODEID: %lu\n", (unsigned long) arg->nodeid);
                 fflush(stdout);
             }
         }
@@ -608,17 +652,18 @@ static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name)
     }
     res2 = send_reply(f, in, res, &arg, sizeof(arg));
     if (res == 0 && res2 == -ENOENT)
-        destroy_node(f, arg.nodeid, in->unique);
+        forget_node(f, arg.nodeid, in->unique);
 }
 
 static void do_forget(struct fuse *f, struct fuse_in_header *in,
                       struct fuse_forget_in *arg)
 {
     if (f->flags & FUSE_DEBUG) {
-        printf("FORGET %li/%i\n", in->nodeid, arg->version);
+        printf("FORGET %lu/%llu\n", (unsigned long) in->nodeid,
+               arg->version);
         fflush(stdout);
     }
-    destroy_node(f, in->nodeid, arg->version);
+    forget_node(f, in->nodeid, arg->version);
 }
 
 static void do_getattr(struct fuse *f, struct fuse_in_header *in)
@@ -644,10 +689,6 @@ static void do_getattr(struct fuse *f, struct fuse_in_header *in)
         convert_stat(&buf, &arg.attr);
         if (!(f->flags & FUSE_USE_INO))
             arg.attr.ino = in->nodeid;
-        else {
-            struct node *node = get_node(f, in->nodeid);
-            node->ino = arg.attr.ino;
-        }
     }
 
     send_reply(f, in, res, &arg, sizeof(arg));
@@ -737,10 +778,6 @@ static void do_setattr(struct fuse *f, struct fuse_in_header *in,
                     convert_stat(&buf, &outarg.attr);
                     if (!(f->flags & FUSE_USE_INO))
                         outarg.attr.ino = in->nodeid;
-                    else {
-                        struct node *node = get_node(f, in->nodeid);
-                        node->ino = outarg.attr.ino;
-                    }
                 }
             }
         }
@@ -826,7 +863,7 @@ static void do_mknod(struct fuse *f, struct fuse_in_header *in,
     }
     res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
     if (res == 0 && res2 == -ENOENT)
-        destroy_node(f, outarg.nodeid, in->unique);
+        forget_node(f, outarg.nodeid, in->unique);
 }
 
 static void do_mkdir(struct fuse *f, struct fuse_in_header *in,
@@ -855,7 +892,7 @@ static void do_mkdir(struct fuse *f, struct fuse_in_header *in,
     }
     res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
     if (res == 0 && res2 == -ENOENT)
-        destroy_node(f, outarg.nodeid, in->unique);
+        forget_node(f, outarg.nodeid, in->unique);
 }
 
 static void do_unlink(struct fuse *f, struct fuse_in_header *in, char *name)
@@ -878,6 +915,7 @@ static void do_unlink(struct fuse *f, struct fuse_in_header *in, char *name)
                 res = f->op.unlink(path);
                 if (res == 0)
                     remove_node(f, in->nodeid, name);
+
             }
         }
         free(path);
@@ -933,7 +971,7 @@ static void do_symlink(struct fuse *f, struct fuse_in_header *in, char *name,
     }
     res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
     if (res == 0 && res2 == -ENOENT)
-        destroy_node(f, outarg.nodeid, in->unique);
+        forget_node(f, outarg.nodeid, in->unique);
 
 }
 
@@ -1008,7 +1046,7 @@ static void do_link(struct fuse *f, struct fuse_in_header *in,
     }
     res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
     if (res == 0 && res2 == -ENOENT)
-        destroy_node(f, outarg.nodeid, in->unique);
+        forget_node(f, outarg.nodeid, in->unique);
 }
 
 static void do_open(struct fuse *f, struct fuse_in_header *in,
@@ -1025,40 +1063,36 @@ static void do_open(struct fuse *f, struct fuse_in_header *in,
     path = get_path(f, in->nodeid);
     if (path != NULL) {
         res = -ENOSYS;
-        if (f->op.open.curr) {
+        if (f->op.open) {
             if (!f->compat)
-                res = f->op.open.curr(path, &fi);
+                res = f->op.open(path, &fi);
             else
-                res = f->op.open.compat2(path, fi.flags);
+                res = ((struct fuse_operations_compat2 *) &f->op)->open(path, fi.flags);
         }
     }
     if (res == 0) {
         int res2;
-
-        /* If the request is interrupted the lock must be held until
-           the cancellation is finished.  Otherwise there could be
-           races with rename/unlink, against which the kernel can't
-           protect */
-        pthread_mutex_lock(&f->lock);
         outarg.fh = fi.fh;
         if (f->flags & FUSE_DEBUG) {
-            printf("OPEN[%lu] flags: 0x%x\n", outarg.fh, arg->flags);
+            printf("OPEN[%lu] flags: 0x%x\n", fi.fh, arg->flags);
             fflush(stdout);
         }
-
-        res2 = send_reply_locked(f, in, res, &outarg, sizeof(outarg));
+        
+        pthread_mutex_lock(&f->lock);
+        res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
         if(res2 == -ENOENT) {
             /* The open syscall was interrupted, so it must be cancelled */
-            if(f->op.release.curr) {
+            if(f->op.release) {
                 if (!f->compat)
-                    f->op.release.curr(path, &fi);
+                    f->op.release(path, &fi);
                 else
-                    f->op.release.compat2(path, fi.flags);
+                    ((struct fuse_operations_compat2 *) &f->op)->release(path, fi.flags);
             }
-        } else
-            get_node(f, in->nodeid)->open_count ++;
+        } else {
+            struct node *node = get_node(f, in->nodeid);
+            node->open_count ++;
+        }
         pthread_mutex_unlock(&f->lock);
-
     } else
         send_reply(f, in, res, NULL, 0);
 
@@ -1079,7 +1113,7 @@ static void do_flush(struct fuse *f, struct fuse_in_header *in,
     path = get_path(f, in->nodeid);
     if (path != NULL) {
         if (f->flags & FUSE_DEBUG) {
-            printf("FLUSH[%lu]\n", arg->fh);
+            printf("FLUSH[%lu]\n", (unsigned long) arg->fh);
             fflush(stdout);
         }
         res = -ENOSYS;
@@ -1096,6 +1130,7 @@ static void do_release(struct fuse *f, struct fuse_in_header *in,
     struct node *node;
     char *path;
     struct fuse_file_info fi;
+    int unlink_hidden;
 
     memset(&fi, 0, sizeof(fi));
     fi.flags = arg->flags;
@@ -1103,28 +1138,29 @@ static void do_release(struct fuse *f, struct fuse_in_header *in,
 
     pthread_mutex_lock(&f->lock);
     node = get_node(f, in->nodeid);
+    assert(node->open_count > 0);
     --node->open_count;
+    unlink_hidden = (node->is_hidden && !node->open_count);
     pthread_mutex_unlock(&f->lock);
 
     path = get_path(f, in->nodeid);
-    if (path != NULL) {
-        if (f->flags & FUSE_DEBUG) {
-            printf("RELEASE[%lu]\n", arg->fh);
-            fflush(stdout);
-        }
-        if (f->op.release.curr) {
-            if (!f->compat)
-                f->op.release.curr(path, &fi);
-            else
-                f->op.release.compat2(path, fi.flags);
-        }
-
-        if(node->is_hidden && node->open_count == 0)
-            /* can now clean up this hidden file */
-            f->op.unlink(path);
-        
-        free(path);
+    if (f->flags & FUSE_DEBUG) {
+        printf("RELEASE[%lu] flags: 0x%x\n", fi.fh, fi.flags);
+        fflush(stdout);
+    }
+    if (f->op.release) {
+        if (!f->compat)
+            f->op.release(path ? path : "-", &fi);
+        else if (path)
+            ((struct fuse_operations_compat2 *) &f->op)->release(path, fi.flags);
     }
+    
+    if(unlink_hidden && path)
+        f->op.unlink(path);
+    
+    if (path)
+        free(path);
+
     send_reply(f, in, 0, NULL, 0);
 }
 
@@ -1150,8 +1186,8 @@ static void do_read(struct fuse *f, struct fuse_in_header *in,
         path = get_path(f, in->nodeid);
         if (path != NULL) {
             if (f->flags & FUSE_DEBUG) {
-                printf("READ[%lu] %u bytes from %llu\n", arg->fh, arg->size,
-                       arg->offset);
+                printf("READ[%lu] %u bytes from %llu\n",
+                       (unsigned long) arg->fh, arg->size, arg->offset);
                 fflush(stdout);
             }
             
@@ -1166,7 +1202,8 @@ static void do_read(struct fuse *f, struct fuse_in_header *in,
             size = res;
             res = 0;
             if (f->flags & FUSE_DEBUG) {
-                printf("   READ[%lu] %u bytes\n", arg->fh, size);
+                printf("   READ[%lu] %u bytes\n", (unsigned long) arg->fh,
+                       size);
                 fflush(stdout);
             }
         }
@@ -1175,7 +1212,7 @@ static void do_read(struct fuse *f, struct fuse_in_header *in,
         out->error = res;
         outsize = sizeof(struct fuse_out_header) + size;
         
-        send_reply_raw(f, outbuf, outsize, 0);
+        send_reply_raw(f, outbuf, outsize);
         free(outbuf);
     }
 }
@@ -1190,15 +1227,15 @@ static void do_write(struct fuse *f, struct fuse_in_header *in,
 
     memset(&fi, 0, sizeof(fi));
     fi.fh = arg->fh;
-    fi.writepage = arg->writepage;
+    fi.writepage = arg->write_flags & 1;
 
     res = -ENOENT;
     path = get_path(f, in->nodeid);
     if (path != NULL) {
         if (f->flags & FUSE_DEBUG) {
             printf("WRITE%s[%lu] %u bytes to %llu\n",
-                   arg->writepage ? "PAGE" : "", arg->fh, arg->size,
-                   arg->offset);
+                   (arg->write_flags & 1) ? "PAGE" : "",
+                   (unsigned long) arg->fh, arg->size, arg->offset);
             fflush(stdout);
         }
 
@@ -1253,13 +1290,13 @@ static void do_statfs(struct fuse *f, struct fuse_in_header *in)
     struct statfs buf;
 
     memset(&buf, 0, sizeof(struct statfs));
-    if (f->op.statfs.curr) {
+    if (f->op.statfs) {
         if (!f->compat || f->compat > 11)
-            res = f->op.statfs.curr("/", &buf);
+            res = f->op.statfs("/", &buf);
         else {
             struct fuse_statfs_compat1 compatbuf;
             memset(&compatbuf, 0, sizeof(struct fuse_statfs_compat1));
-            res = f->op.statfs.compat1(&compatbuf);
+            res = ((struct fuse_operations_compat1 *) &f->op)->statfs(&compatbuf);
             if (res == 0)
                 convert_statfs_compat(&compatbuf, &buf);
         }
@@ -1287,12 +1324,12 @@ static void do_fsync(struct fuse *f, struct fuse_in_header *in,
     path = get_path(f, in->nodeid);
     if (path != NULL) {
         if (f->flags & FUSE_DEBUG) {
-            printf("FSYNC[%lu]\n", inarg->fh);
+            printf("FSYNC[%lu]\n", (unsigned long) inarg->fh);
             fflush(stdout);
         }
         res = -ENOSYS;
         if (f->op.fsync)
-            res = f->op.fsync(path, inarg->datasync, &fi);
+            res = f->op.fsync(path, inarg->fsync_flags & 1, &fi);
         free(path);
     }
     send_reply(f, in, res, NULL, 0);
@@ -1355,7 +1392,7 @@ static void do_getxattr_read(struct fuse *f, struct fuse_in_header *in,
         out->unique = in->unique;
         out->error = res;
         
-        send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size, 0);
+        send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size);
         free(outbuf);
     }
 }
@@ -1423,7 +1460,7 @@ static void do_listxattr_read(struct fuse *f, struct fuse_in_header *in,
         out->unique = in->unique;
         out->error = res;
         
-        send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size, 0);
+        send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size);
         free(outbuf);
     }
 }
@@ -1481,12 +1518,12 @@ void fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd)
     size_t argsize;
     struct fuse_context *ctx = fuse_get_context();
 
-    dec_avail(f);
+    fuse_dec_avail(f);
 
     if ((f->flags & FUSE_DEBUG)) {
-        printf("unique: %i, opcode: %s (%i), nodeid: %li, insize: %i\n",
-               in->unique, opname(in->opcode), in->opcode, in->nodeid,
-               cmd->buflen);
+        printf("unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %i\n",
+               in->unique, opname(in->opcode), in->opcode,
+               (unsigned long) in->nodeid, cmd->buflen);
         fflush(stdout);
     }
 
@@ -1790,9 +1827,9 @@ struct fuse *fuse_new_common(int fd, const char *opts,
     struct fuse *f;
     struct node *root;
 
-    if (sizeof(struct fuse_operations_i) < op_size) {
+    if (sizeof(struct fuse_operations) < op_size) {
         fprintf(stderr, "fuse: warning: library too old, some operations may not not work\n");
-        op_size = sizeof(struct fuse_operations_i);
+        op_size = sizeof(struct fuse_operations);
     }
 
     f = (struct fuse *) calloc(1, sizeof(struct fuse));
@@ -1829,12 +1866,14 @@ struct fuse *fuse_new_common(int fd, const char *opts,
 
 #ifndef USE_UCLIBC
      pthread_mutex_init(&f->lock, NULL);
+     pthread_mutex_init(&f->worker_lock, NULL);
 #else
      {
          pthread_mutexattr_t attr;
          pthread_mutexattr_init(&attr);
          pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
          pthread_mutex_init(&f->lock, &attr);
+         pthread_mutex_init(&f->worker_lock, &attr);
          pthread_mutexattr_destroy(&attr);
      }
 #endif
@@ -1850,8 +1889,6 @@ struct fuse *fuse_new_common(int fd, const char *opts,
         goto out_free_id_table;
     }
 
-    root->mode = 0;
-    root->rdev = 0;
     root->name = strdup("/");
     if (root->name == NULL) {
         fprintf(stderr, "fuse: memory allocation failed\n");
@@ -1861,6 +1898,7 @@ struct fuse *fuse_new_common(int fd, const char *opts,
     root->parent = 0;
     root->nodeid = FUSE_ROOT_ID;
     root->generation = 0;
+    root->refctr = 1;
     hash_id(f, root);
 
     return f;
@@ -1926,6 +1964,7 @@ void fuse_destroy(struct fuse *f)
     free(f->id_table);
     free(f->name_table);
     pthread_mutex_destroy(&f->lock);
+    pthread_mutex_destroy(&f->worker_lock);
     free(f);
 }
 
index f3c307622459347bf92c719992ed84d3ebe25ee2..ebe712a9e9d34251042d1a26411071706be465a5 100644 (file)
@@ -7,83 +7,14 @@
 */
 
 #include "fuse.h"
-#include "fuse_compat.h"
-#include <stdio.h>
 #include <pthread.h>
 
-/* FUSE flags: */
-
-/** Enable debuging output */
-#define FUSE_DEBUG       (1 << 1)
-
-/** If a file is removed but it's still open, don't hide the file but
-    remove it immediately */
-#define FUSE_HARD_REMOVE (1 << 2)
-
-/** Use st_ino field in getattr instead of generating inode numbers  */
-#define FUSE_USE_INO     (1 << 3)
-
-
 typedef unsigned long nodeid_t;
 
-struct node {
-    struct node *name_next;
-    struct node *id_next;
-    nodeid_t nodeid;
-    unsigned int generation;
-    nodeid_t parent;
-    char *name;
-    int mode;
-    int rdev;
-    unsigned long ino;
-    int version;
-    int open_count;
-    int is_hidden;
-};
-
-struct fuse_operations_i {
-    int (*getattr)     (const char *, struct stat *);
-    int (*readlink)    (const char *, char *, size_t);
-    int (*getdir)      (const char *, fuse_dirh_t, fuse_dirfil_t);
-    int (*mknod)       (const char *, mode_t, dev_t);
-    int (*mkdir)       (const char *, mode_t);
-    int (*unlink)      (const char *);
-    int (*rmdir)       (const char *);
-    int (*symlink)     (const char *, const char *);
-    int (*rename)      (const char *, const char *);
-    int (*link)        (const char *, const char *);
-    int (*chmod)       (const char *, mode_t);
-    int (*chown)       (const char *, uid_t, gid_t);
-    int (*truncate)    (const char *, off_t);
-    int (*utime)       (const char *, struct utimbuf *);
-    union {
-        int (*curr)    (const char *, struct fuse_file_info *);
-        int (*compat2) (const char *, int);
-    } open;
-    int (*read)        (const char *, char *, size_t, off_t,
-                        struct fuse_file_info *);
-    int (*write)       (const char *, const char *, size_t, off_t,
-                        struct fuse_file_info *);
-    union {
-        int (*curr)    (const char *, struct statfs *);
-        int (*compat1) (struct fuse_statfs_compat1 *);
-    } statfs;
-    int (*flush)       (const char *, struct fuse_file_info *);
-    union {
-        int (*curr)    (const char *, struct fuse_file_info *);
-        int (*compat2) (const char *, int);
-    } release;
-    int (*fsync)       (const char *, int, struct fuse_file_info *);
-    int (*setxattr)    (const char *, const char *, const char *, size_t, int);
-    int (*getxattr)    (const char *, const char *, char *, size_t);
-    int (*listxattr)   (const char *, char *, size_t);
-    int (*removexattr) (const char *, const char *);
-};
-
 struct fuse {
     int flags;
     int fd;
-    struct fuse_operations_i op;
+    struct fuse_operations op;
     int compat;
     struct node **name_table;
     size_t name_table_size;
@@ -93,6 +24,7 @@ struct fuse {
     unsigned int generation;
     unsigned int hidectr;
     pthread_mutex_t lock;
+    pthread_mutex_t worker_lock;
     int numworker;
     int numavail;
     volatile int exited;
@@ -100,17 +32,6 @@ struct fuse {
     int minorver;
 };
 
-struct fuse_dirhandle {
-    struct fuse *fuse;
-    nodeid_t dir;
-    FILE *fp;
-};
-
-struct fuse_cmd {
-    char *buf;
-    size_t buflen;
-};
-
 struct fuse *fuse_new_common(int fd, const char *opts,
                              const struct fuse_operations *op,
                              size_t op_size, int compat);
index 94d3bb8413623cfd9ec3b21a5aac501a2ae6c37e..c02d5c496607bf769fd5cb995aaa720fb88b2856 100644 (file)
@@ -40,9 +40,9 @@ static void *do_work(void *data)
     ctx = (struct fuse_context *) malloc(sizeof(struct fuse_context));
     if (ctx == NULL) {
         fprintf(stderr, "fuse: failed to allocate fuse context\n");
-        pthread_mutex_lock(&f->lock);
+        pthread_mutex_lock(&f->worker_lock);
         f->numavail --;
-        pthread_mutex_unlock(&f->lock);
+        pthread_mutex_unlock(&f->worker_lock);
         return NULL;
     }
     pthread_setspecific(context_key, ctx);
@@ -61,7 +61,7 @@ static void *do_work(void *data)
             continue;
 
         if (f->numavail == 0 && f->numworker < FUSE_MAX_WORKERS) {
-            pthread_mutex_lock(&f->lock);
+            pthread_mutex_lock(&f->worker_lock);
             if (f->numworker < FUSE_MAX_WORKERS) {
                 /* FIXME: threads should be stored in a list instead
                    of an array */
@@ -69,15 +69,15 @@ static void *do_work(void *data)
                 pthread_t *thread_id = &w->threads[f->numworker];
                 f->numavail ++;
                 f->numworker ++;
-                pthread_mutex_unlock(&f->lock);
+                pthread_mutex_unlock(&f->worker_lock);
                 res = start_thread(w, thread_id);
                 if (res == -1) {
-                    pthread_mutex_lock(&f->lock);
+                    pthread_mutex_lock(&f->worker_lock);
                     f->numavail --;
-                    pthread_mutex_unlock(&f->lock);
+                    pthread_mutex_unlock(&f->worker_lock);
                 }
             } else
-                pthread_mutex_unlock(&f->lock);
+                pthread_mutex_unlock(&f->worker_lock);
         }
 
         w->proc(w->f, cmd, w->data);
index c14248d68b76fabe6991587d3ba785feb18ace8a..25a60aafdcbb8c8849b611399fe32d0c7a73264e 100644 (file)
@@ -7,6 +7,7 @@
 */
 
 #include "fuse_i.h"
+#include "fuse_compat.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -90,6 +91,19 @@ static int set_signal_handlers()
     return 0;
 }
 
+static int opt_member(const char *opts, const char *opt)
+{
+    const char *e, *s = opts;
+    int optlen = strlen(opt);
+    for (s = opts; s; s = e + 1) {
+        if(!(e = strchr(s, ',')))
+            break;
+        if (e - s == optlen && strncmp(s, opt, optlen) == 0)
+            return 1;
+    }
+    return (s && strcmp(s, opt) == 0);
+}
+
 static int add_option_to(const char *opt, char **optp)
 {
     unsigned len = strlen(opt);
@@ -187,7 +201,6 @@ static int fuse_parse_cmdline(int argc, const char *argv[], char **kernel_opts,
                     res = add_options(lib_opts, kernel_opts, "debug");
                     if (res == -1)
                         goto err;
-                    *background = 0;
                     break;
                     
                 case 'r':
@@ -251,7 +264,6 @@ static int fuse_parse_cmdline(int argc, const char *argv[], char **kernel_opts,
     free(*mountpoint);
     return -1;
 }
-                              
 
 static struct fuse *fuse_setup_common(int argc, char *argv[],
                                       const struct fuse_operations *op,
@@ -286,7 +298,7 @@ static struct fuse *fuse_setup_common(int argc, char *argv[],
     if (fuse == NULL)
         goto err_unmount;
 
-    if (background) {
+    if (background && !opt_member(lib_opts, "debug")) {
         res = daemon(0, 0);
         if (res == -1) {
             perror("fuse: failed to daemonize program\n");