cleanup
authorMiklos Szeredi <miklos@szeredi.hu>
Sun, 12 Dec 2004 11:45:24 +0000 (11:45 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Sun, 12 Dec 2004 11:45:24 +0000 (11:45 +0000)
17 files changed:
ChangeLog
Filesystems
include/Makefile.am
include/fuse.h
include/old/fuse.h [new file with mode: 0644]
kernel/cleanup.sh
kernel/dev.c
kernel/dir.c
kernel/file.c
kernel/fuse_i.h
kernel/fuse_kernel.h
kernel/inode.c
kernel/util.c
lib/fuse.c
lib/fuse_versionscript
lib/helper.c
util/fusermount.c

index 66e88c55a2f4c4f2f015376571fc46626c34be7a..7029c0a3718d54e350ab9ad854dc14f42570d71f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,43 @@
+2004-12-12  Miklos Szeredi <miklos@szeredi.hu>
+
+       * KERNEL: lots of cleanups related to avoiding possible deadlocks.
+       These will cause some regressions, but stability is considered
+       more important.  If any of these features turns out to be
+       important, it can be readded with the deadlock problems addressed.
+       
+       * Make all requests interruptible (only with SIGKILL currently).
+       This can be used to break any deadlock produced by the userspace
+       filesystem accessing it's own exported files.  The RELEASE request
+       is special, because if it's interrupted before sending it to
+       userspace it is still sent, but the reply is not awaited.
+
+       * If request is interrupted before being sent to userspace, and if
+       it hasn't yet got any side effects, it is always restarted,
+       regardless of the SA_RESTART flag.  This makes these interruptions
+       transparent to the process.
+
+       * Remove shared-writable mmap support, which was prone to an
+       out-of-memory deadlock situation
+
+       * Remove INVALIDATE userspace initiated request
+
+       * Make readpages() synchronous.  Asynchronous requests are
+       deadlock prone, since they cannot be interrupted.
+
+       * Add readv/writev support to fuse device operations
+
+       * Remove some printks, which userspace FS can use for a DoS
+       against syslog
+
+       * Remove 'large_read' mount option from 2.6 in kernel, check it in
+       fusermount instead
+
+       * LIB: improve compatibility with a fuse.h header installed in
+       ${prefix}/include which in turn includes the real header.
+
+       * LIB: improve compatibility by defining fuse_main() (which is now
+       not used), so old configure scripts find it.
+
 2004-12-10  Miklos Szeredi <miklos@szeredi.hu>
 
        * When mounting on a subdirectory of / don't duplicate slashes at
index 720e3d0ab444f7d8a165cbf75f3541c0618486a5..553b714172f176fd4ad3d5cde25f1fc85962c2cb 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,8 +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
 
@@ -25,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
 
@@ -38,7 +37,7 @@ 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/
 
@@ -50,7 +49,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/
 
@@ -62,7 +61,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
 
@@ -77,7 +76,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
 
@@ -92,7 +91,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
 
@@ -105,7 +104,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
 
@@ -118,7 +117,7 @@ Description:
 ==============================================================================
 Name: LUFS bridge (alpha)
 
-Author: Miklos Szeredi <mszeredi at inf bme hu>
+Author: Miklos Szeredi     miklos at szeredi hu
 
 Homepage: http://sourceforge.net/project/showfiles.php?group_id=121684&package_id=132803
 
@@ -131,7 +130,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
 
@@ -145,7 +144,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
 
@@ -160,7 +159,7 @@ Description:
 ==============================================================================
 Name: Fusedav
 
-Author: Lennart Poettering <mzshfrqni (at) 0pointer (dot) de>
+Author: Lennart Poettering     mzshfrqni at 0pointer de
 
 Homepage: http://0pointer.de/lennart/projects/fusedav/
 
@@ -173,7 +172,7 @@ Description:
 ==============================================================================
 Name: RelFS
 
-Author: Vincenzo Ciancia <vincenzo_ml (at) yahoo (dot) it>
+Author: Vincenzo Ciancia     vincenzo_ml at yahoo it
 
 Homepage: http://relfs.sourceforge.net/
 
@@ -187,7 +186,7 @@ Description:
 ==============================================================================
 Name: GmailFS
 
-Author: Richard Jones <richard (at) jones (dot) name>
+Author: Richard Jones     richard at jones name
 
 Homepage: http://richard.jones.name/google-hacks/gmail-filesystem/gmail-filesystem.html
 
@@ -201,7 +200,7 @@ Description:
 ==============================================================================
 Name: DataDraw
 
-Author: Bill Cox <bill (at) viasic (dot) com>
+Author: Bill Cox     bill at viasic com
 
 Homepage: http://www.viasic.com/opensource/
 
@@ -213,7 +212,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/
 
@@ -225,3 +224,33 @@ Description:
   gphoto2"
 
 ==============================================================================
+Name: cvsfs-fuse
+
+Author: Patrick Frank     pfrank at gmx de
+
+Homepage: http://sourceforge.net/projects/cvsfs
+
+Description:
+
+  This provides a package which presents the CVS contents as mountable
+  file system. It allows to view the versioned files as like they were
+  ordinary files on a disk. There is also a possibility to check
+  in/out some files for editing.
+
+==============================================================================
+Name: Wayback (User-level Versioning File System for Linux)
+
+Author: Brian Cornell     techie at northwestern edu
+
+Homepage: http://wayback.sourceforge.net/
+
+Description:
+
+  When you use a Wayback file system, old versions of files are never
+  lost. No matter how much you change a file or directory, everything
+  is always kept in a versioning file so that you never lose important
+  data. Wayback provides the ability to remount any already mounted
+  file system with versioning support under a different
+  directory.
+
+==============================================================================
index 14ac9d91d1357529e5bf4db35bee8b571cd7ed93..269e0e1701c27a4761f83dd1d218d55c1fc82470 100644 (file)
@@ -2,11 +2,5 @@
 
 fuseincludedir=$(includedir)/fuse
 fuseinclude_HEADERS = fuse.h fuse_compat.h
+include_HEADERS = old/fuse.h
 noinst_HEADERS = fuse_kernel.h
-
-# remove fuse.h from old place to avoid collision with new one
-install-data-local:
-       @if test -f $(includedir)/fuse.h; then \
-               echo "Removing old FUSE header from $(includedir)/fuse.h"; \
-               rm -f $(includedir)/fuse.h; \
-       fi
index 8d2c274d69c66318b5d1414dc0a57f79974b6929..a0a6a70423e97b9705b76709e5cf725334d5165c 100644 (file)
@@ -83,6 +83,11 @@ struct fuse_file_info {
  * file system operations.  A major exception is that instead of
  * returning an error in 'errno', the operation should return the
  * negated error value (-errno) directly. 
+ *
+ * All methods are optional, but some are essential for a useful
+ * filesystem (e.g. getattr).  Flush, release and fsync are special
+ * purpose methods, without which a full featured filesystem can still
+ * be implemented.
  */
 struct fuse_operations {
     /** Get file attributes.
@@ -187,12 +192,13 @@ struct fuse_operations {
     /** Possibly flush cached data 
      * 
      * BIG NOTE: This is not equivalent to fsync().  It's not a
-     * request to sync dirty data, and can safely be ignored.
+     * request to sync dirty data.
      *
      * Flush is called on each close() of a file descriptor.  So if a
      * filesystem wants to return write errors in close() and the file
      * has cached dirty data, this is a good place to write back data
-     * and return any errors.
+     * and return any errors.  Since many applications ignore close()
+     * errors this is not always useful.
      * 
      * NOTE: The flush() method may be called more than once for each
      * open().  This happens if more than one file descriptor refers
@@ -210,10 +216,10 @@ struct fuse_operations {
      * are unmapped.
      *
      * For every open() call there will be exactly one release() call
-     * with the same flags.  It is possible to have a file opened more
-     * than once, in which case only the last release will mean, that
-     * no more reads/writes will happen on the file.  The return value
-     * of release is ignored.  Implementing this method is optional.
+     * with the same flags and file descriptor.  It is possible to
+     * have a file opened more than once, in which case only the last
+     * release will mean, that no more reads/writes will happen on the
+     * file.  The return value of release is ignored.
      */
     int (*release) (const char *, struct fuse_file_info *);
 
@@ -442,7 +448,7 @@ void fuse_set_getcontext_func(struct fuse_context *(*func)(void));
  * ----------------------------------------------------------- */
 
 #if FUSE_USE_VERSION == 21 || FUSE_USE_VERSION == 11
-#  include <fuse_compat.h>
+#  include "fuse_compat.h"
 #  define fuse_dirfil_t fuse_dirfil_t_compat
 #  define __fuse_read_cmd fuse_read_cmd
 #  define __fuse_process_cmd fuse_process_cmd
diff --git a/include/old/fuse.h b/include/old/fuse.h
new file mode 100644 (file)
index 0000000..3db0945
--- /dev/null
@@ -0,0 +1,9 @@
+/*
+   This header is for compatibility with older software using FUSE.
+
+   Please use 'pkg-config --cflags fuse' to set include path.  The
+   correct usage is still '#include <fuse.h>', not '#include
+   <fuse/fuse.h>'.
+*/
+
+#include "fuse/fuse.h"
index fb0807e0d15101f16a16758e65da7b29e7a2378d..9d7b92b25e84c17c595eed30d415b4142579ea9f 100755 (executable)
@@ -11,6 +11,11 @@ if test "$destdir" = "."; then
     exit 1
 fi
 
+mkdir -p $destdir/fs/fuse
+mkdir -p $destdir/include/linux
+
+
 for f in dev.c dir.c file.c inode.c util.c fuse_i.h; do
-    unifdef -DKERNEL_2_6 -DKERNEL_2_6_6_PLUS -DKERNEL_2_6_10_PLUS -DHAVE_KERNEL_XATTR -DFS_SAFE -DMAX_LFS_FILESIZE -DFUSE_MAINLINE -DBUG_ON -DHAVE_FS_SUBSYS -D__user -DMODULE_LICENSE $f > $destdir/$f
+    unifdef -DKERNEL_2_6 -DKERNEL_2_6_6_PLUS -DKERNEL_2_6_10_PLUS -DHAVE_KERNEL_XATTR -DFS_SAFE -DMAX_LFS_FILESIZE -DFUSE_MAINLINE -DBUG_ON -DHAVE_FS_SUBSYS -D__user -DMODULE_LICENSE $f > $destdir/fs/fuse/$f
 done
+cp fuse_kernel.h $destdir/include/linux/fuse.h
index 220918a1da3dc78f36f57e9354c4a521d1874b17..b2a1b90b6c9cd1508211ecc2dfcabded1553fa64 100644 (file)
@@ -1,9 +1,9 @@
 /*
-    FUSE: Filesystem in Userspace
-    Copyright (C) 2001-2004  Miklos Szeredi <miklos@szeredi.hu>
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2004  Miklos Szeredi <miklos@szeredi.hu>
 
-    This program can be distributed under the terms of the GNU GPL.
-    See the file COPYING.
+  This program can be distributed under the terms of the GNU GPL.
+  See the file COPYING.
 */
 
 #include "fuse_i.h"
@@ -11,6 +11,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/poll.h>
+#include <linux/uio.h>
 #ifdef KERNEL_2_6
 #include <linux/kobject.h>
 #else
@@ -38,6 +39,7 @@ static inline void fuse_request_init(struct fuse_req *req)
        memset(req, 0, sizeof(*req));
        INIT_LIST_HEAD(&req->list);
        init_waitqueue_head(&req->waitq);
+       atomic_set(&req->count, 1);
 }
 
 struct fuse_req *fuse_request_alloc(void)
@@ -53,29 +55,66 @@ void fuse_request_free(struct fuse_req *req)
        kmem_cache_free(fuse_req_cachep, req);
 }
 
-/* Called with fuse_lock held.  Releases, and then reaquires it. */
-static void request_wait_answer(struct fuse_req *req)
-{
-       spin_unlock(&fuse_lock);
-       wait_event(req->waitq, req->finished);
-       spin_lock(&fuse_lock);
-}
-
 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)
+{
+       sigset_t sigmask;
+
+       siginitsetinv(&sigmask, sigmask(SIGKILL));
+       sigprocmask(SIG_BLOCK, &sigmask, oldset);
+}
+
+static inline void restore_sigs(sigset_t *oldset)
+{
+       sigprocmask(SIG_SETMASK, oldset, NULL);
+}
+#else
+static inline void block_sigs(sigset_t *oldset)
+{
+       spin_lock_irq(&current->sigmask_lock);
+       *oldset = current->blocked;
+       siginitsetinv(&current->blocked, sigmask(SIGKILL) & ~oldset->sig[0]);
+       recalc_sigpending(current);
+       spin_unlock_irq(&current->sigmask_lock);
+}
+
+static inline void restore_sigs(sigset_t *oldset)
+{
+       spin_lock_irq(&current->sigmask_lock);
+       current->blocked = *oldset;
+       recalc_sigpending(current);
+       spin_unlock_irq(&current->sigmask_lock);
+}
+#endif
+
 void fuse_reset_request(struct fuse_req *req)
 {
        int preallocated = req->preallocated;
+       BUG_ON(atomic_read(&req->count) != 1);
        fuse_request_init(req);
        req->preallocated = preallocated;
 }
 
+static void __fuse_get_request(struct fuse_req *req)
+{
+       atomic_inc(&req->count);
+}
+
+/* Must be called with > 1 refcount */
+static void __fuse_put_request(struct fuse_req *req)
+{
+       atomic_dec(&req->count);
+}
+
 static struct fuse_req *do_get_request(struct fuse_conn *fc)
 {
        struct fuse_req *req;
@@ -85,7 +124,8 @@ static struct fuse_req *do_get_request(struct fuse_conn *fc)
        req = list_entry(fc->unused_list.next, struct fuse_req, list);
        list_del_init(&req->list);
        spin_unlock(&fuse_lock);
-       fuse_reset_request(req);
+       fuse_request_init(req);
+       req->preallocated = 1;
        req->in.h.uid = current->fsuid;
        req->in.h.gid = current->fsgid;
        req->in.h.pid = current->pid;
@@ -101,18 +141,16 @@ struct fuse_req *fuse_get_request(struct fuse_conn *fc)
 
 struct fuse_req *fuse_get_request_nonint(struct fuse_conn *fc)
 {
-       down(&fc->unused_sem);
-       return do_get_request(fc);
-}
+       int intr;
+       sigset_t oldset;
 
-struct fuse_req *fuse_get_request_nonblock(struct fuse_conn *fc)
-{
-       if (down_trylock(&fc->unused_sem))
-               return NULL;
-       return  do_get_request(fc);
+       block_sigs(&oldset);
+       intr = down_interruptible(&fc->unused_sem);
+       restore_sigs(&oldset);
+       return intr ? NULL : do_get_request(fc);
 }
 
-void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
+void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req)
 {
        if (!req->preallocated)
                fuse_request_free(req);
@@ -124,61 +162,114 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
        }
 }
 
-/* Must be called with fuse_lock held, and unlocks it */
+void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
+{
+       if (atomic_dec_and_test(&req->count))
+               fuse_putback_request(fc, req);
+}
+
+/* Called with fuse_lock, unlocks it */
 static void request_end(struct fuse_conn *fc, struct fuse_req *req)
 {
-       fuse_reqend_t endfunc = req->end;
+       int putback;
+       req->finished = 1;
+       putback = atomic_dec_and_test(&req->count);
+       spin_unlock(&fuse_lock);
+       wake_up(&req->waitq);
+       if (putback)
+               fuse_putback_request(fc, req);
+}
+
+static int request_wait_answer_nonint(struct fuse_req *req)
+{
+       int err;
+       sigset_t oldset;
+       block_sigs(&oldset);
+       err = wait_event_interruptible(req->waitq, req->finished);
+       restore_sigs(&oldset);
+       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)
+{
+       int intr;
 
-       if (!endfunc) {
-               wake_up(&req->waitq);
+       spin_unlock(&fuse_lock);
+       if (interruptible)
+               intr = wait_event_interruptible(req->waitq, req->finished);
+       else
+               intr = request_wait_answer_nonint(req);
+       spin_lock(&fuse_lock);
+       if (intr && interruptible && req->sent) {
+               /* If request is already in userspace, only allow KILL
+                  signal to interrupt */
                spin_unlock(&fuse_lock);
-       } else {
+               intr = request_wait_answer_nonint(req);
+               spin_lock(&fuse_lock);
+       }
+       if (!intr)
+               return;
+
+       if (background && !req->sent) {
+               req->isreply = 0;
+               return;
+       }
+       req->out.h.error = -ERESTARTNOINTR;
+       req->interrupted = 1;
+       if (req->locked) {
+               /* This is uninterruptible sleep, because data is
+                  being copied to/from the buffers of req.  During
+                  locked state, there musn't be any filesystem
+                  operation (e.g. page fault), since that could lead
+                  to deadlock */
                spin_unlock(&fuse_lock);
-               endfunc(fc, req);
+               wait_event(req->waitq, !req->locked);
+               spin_lock(&fuse_lock);
+       }
+       if (!list_empty(&req->list)) {
+               /* request is still on one of the lists */
+               list_del(&req->list);
+               __fuse_put_request(req);
        }
 }
 
-void request_send(struct fuse_conn *fc, struct fuse_req *req)
+static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req,
+                             int interruptible, int background)
 {
        req->isreply = 1;
-       req->end = NULL;
-
        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);
-               request_wait_answer(req);
-               list_del(&req->list);
+               /* acquire extra reference, since request is still needed
+                  after request_end() */
+               __fuse_get_request(req);
+
+               request_wait_answer(req, interruptible, background);
        }
        spin_unlock(&fuse_lock);
 }
 
-void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
+void request_send(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);
-               spin_unlock(&fuse_lock);
-       } else {
-               spin_unlock(&fuse_lock);
-               fuse_put_request(fc, req);
-       }
+       request_send_wait(fc, req, 1, 0);
 }
 
-void request_send_async(struct fuse_conn *fc, struct fuse_req *req,
-                       fuse_reqend_t end)
+void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req,
+                        int background)
 {
-       req->end = end;
-       req->isreply = 1;
+       request_send_wait(fc, req, 0, background);
+}
 
+void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
+{
+       req->isreply = 0;
        spin_lock(&fuse_lock);
        if (fc->file) {
-               req->in.h.unique = get_unique(fc);
                list_add_tail(&req->list, &fc->pending);
                wake_up(&fc->waitq);
                spin_unlock(&fuse_lock);
@@ -188,366 +279,410 @@ void request_send_async(struct fuse_conn *fc, struct fuse_req *req,
        }
 }
 
-static void request_wait(struct fuse_conn *fc)
+static inline int lock_request(struct fuse_req *req)
 {
-       DECLARE_WAITQUEUE(wait, current);
-
-       add_wait_queue_exclusive(&fc->waitq, &wait);
-       while (fc->sb && list_empty(&fc->pending)) {
-               set_current_state(TASK_INTERRUPTIBLE);
-               if (signal_pending(current))
-                       break;
-
-               spin_unlock(&fuse_lock);
-               schedule();
+       int err = 0;
+       if (req) {
                spin_lock(&fuse_lock);
+               if (req->interrupted)
+                       err = -ENOENT;
+               else
+                       req->locked = 1;
+               spin_unlock(&fuse_lock);
        }
-       set_current_state(TASK_RUNNING);
-       remove_wait_queue(&fc->waitq, &wait);
-}
-
-static inline int copy_in_page(char __user *buf, struct page *page,
-                              unsigned offset, unsigned count)
-{
-       char *tmpbuf = kmap(page);
-       int err = copy_to_user(buf, tmpbuf + offset, count);
-       kunmap(page);
        return err;
 }
 
-static int copy_in_pages(struct fuse_req *req, size_t nbytes, char __user *buf)
+static inline void unlock_request(struct fuse_req *req)
 {
-       unsigned i;
-       unsigned offset = req->page_offset;
-       unsigned count = min(nbytes, (unsigned) PAGE_SIZE - offset);
-       for (i = 0; i < req->num_pages && nbytes; i++) {
-               struct page *page = req->pages[i];
-               if (page && copy_in_page(buf, page, offset, count))
-                       return -EFAULT;
-               nbytes -= count;
-               buf += count;
-               count = min(nbytes, (unsigned) PAGE_SIZE);
-               offset = 0;
+       if (req) {
+               spin_lock(&fuse_lock);
+               req->locked = 0;
+               if (req->interrupted)
+                       wake_up(&req->waitq);
+               spin_unlock(&fuse_lock);
        }
-       return 0;
 }
 
-static inline int copy_in_one(struct fuse_req *req, size_t argsize,
-                             const void *val, int islast, char __user *buf,
-                             size_t nbytes)
-{
-       if (nbytes < argsize) {
-               printk("fuse_dev_read: buffer too small\n");
-               return -EINVAL;
-       }
-       if (islast && req->in.argpages)
-               return copy_in_pages(req, argsize, buf);
-       else if (argsize && copy_to_user(buf, val, argsize))
-               return -EFAULT;
-       else
-               return 0;
-}
+struct fuse_copy_state {
+       int write;
+       struct fuse_req *req;
+       const struct iovec *iov;
+       unsigned long nr_segs;
+       unsigned long seglen;
+       unsigned long addr;
+       struct page *pg;
+       void *mapaddr;
+       void *buf;
+       unsigned len;
+};
 
-static int copy_in_args(struct fuse_req *req, char __user *buf, size_t nbytes)
+static unsigned fuse_copy_init(struct fuse_copy_state *cs, int write,
+                              struct fuse_req *req, const struct iovec *iov,
+                              unsigned long nr_segs)
 {
-       int i;
-       int err;
-       struct fuse_in *in = &req->in;
-       size_t orignbytes = nbytes;
-       unsigned argsize;
-
-       argsize = sizeof(in->h);
-       err = copy_in_one(req, argsize, &in->h, 0, buf, nbytes);
-       if (err)
-               return err;
-
-       buf += argsize;
-       nbytes -= argsize;
+       unsigned i;
+       unsigned nbytes;
 
-       for (i = 0; i < in->numargs; i++) {
-               struct fuse_in_arg *arg = &in->args[i];
-               int islast = (i == in->numargs - 1);
-               err = copy_in_one(req, arg->size, arg->value, islast, buf,
-                                 nbytes);
-               if (err)
-                       return err;
+       memset(cs, 0, sizeof(*cs));
+       cs->write = write;
+       cs->req = req;
+       cs->iov = iov;
+       cs->nr_segs = nr_segs;
 
-               buf += arg->size;
-               nbytes -= arg->size;
-       }
+       nbytes = 0;
+       for (i = 0; i < nr_segs; i++)
+               nbytes += iov[i].iov_len;
 
-       return orignbytes - nbytes;
+       return nbytes;
 }
 
-static ssize_t fuse_dev_read(struct file *file, char __user *buf,
-                            size_t nbytes, loff_t *off)
+static inline void fuse_copy_finish(struct fuse_copy_state *cs)
 {
-       ssize_t ret;
-       struct fuse_conn *fc;
-       struct fuse_req *req = NULL;
-
-       spin_lock(&fuse_lock);
-       fc = file->private_data;
-       if (!fc) {
-               spin_unlock(&fuse_lock);
-               return -EPERM;
-       }
-       request_wait(fc);
-       if (!fc->sb)
-               fc = NULL;
-       else if (!list_empty(&fc->pending)) {
-               req = list_entry(fc->pending.next, struct fuse_req, list);
-               list_del_init(&req->list);
-       }
-       spin_unlock(&fuse_lock);
-       if (!fc)
-               return -ENODEV;
-       if (req == NULL)
-               return -EINTR;
-
-       ret = copy_in_args(req, buf, nbytes);
-       spin_lock(&fuse_lock);
-       if (req->isreply) {
-               if (ret < 0) {
-                       req->out.h.error = -EPROTO;
-                       req->finished = 1;
-               } else
-                       list_add_tail(&req->list, &fc->processing);
-               if (ret < 0)
-                       /* Unlocks fuse_lock: */
-                       request_end(fc, req);
-               else
-                       spin_unlock(&fuse_lock);
-       } else {
-               spin_unlock(&fuse_lock);
-               fuse_put_request(fc, req);
+       if (cs->mapaddr) {
+               kunmap_atomic(cs->mapaddr, KM_USER0);
+               if (cs->write) {
+                       flush_dcache_page(cs->pg);
+                       set_page_dirty_lock(cs->pg);
+               }
+               put_page(cs->pg);
+               cs->mapaddr = NULL;
        }
-       return ret;
 }
 
-static struct fuse_req *request_find(struct fuse_conn *fc, unsigned unique)
+static int fuse_copy_fill(struct fuse_copy_state *cs)
 {
-       struct list_head *entry;
-       struct fuse_req *req = NULL;
+       unsigned long offset;
+       int err;
 
-       list_for_each(entry, &fc->processing) {
-               struct fuse_req *tmp;
-               tmp = list_entry(entry, struct fuse_req, list);
-               if (tmp->in.h.unique == unique) {
-                       req = tmp;
-                       break;
-               }
+       unlock_request(cs->req);
+       fuse_copy_finish(cs);
+       if (!cs->seglen) {
+               BUG_ON(!cs->nr_segs);
+               cs->seglen = cs->iov[0].iov_len;
+               cs->addr = (unsigned long) cs->iov[0].iov_base;
+               cs->iov ++;
+               cs->nr_segs --;
        }
+       down_read(&current->mm->mmap_sem);
+       err = get_user_pages(current, current->mm, cs->addr, 1, cs->write, 0,
+                            &cs->pg, NULL);
+       up_read(&current->mm->mmap_sem);
+       if (err < 0)
+               return err;
+       BUG_ON(err != 1);
+       offset = cs->addr % PAGE_SIZE;
+       cs->mapaddr = kmap_atomic(cs->pg, KM_USER0);
+       cs->buf = cs->mapaddr + offset;
+       cs->len = min(PAGE_SIZE - offset, cs->seglen);
+       cs->seglen -= cs->len;
+       cs->addr += cs->len;
 
-       return req;
+       return lock_request(cs->req);
 }
 
-static void process_getdir(struct fuse_req *req)
+static inline int fuse_copy_do(struct fuse_copy_state *cs, void **val,
+                              unsigned *size)
 {
-       struct fuse_getdir_out_i *arg = req->out.args[0].value;
-       arg->file = fget(arg->fd);
+       unsigned ncpy = min(*size, cs->len);
+       if (val) {
+               if (cs->write)
+                       memcpy(cs->buf, *val, ncpy);
+               else
+                       memcpy(*val, cs->buf, ncpy);
+               *val += ncpy;
+       }
+       *size -= ncpy;
+       cs->len -= ncpy;
+       cs->buf += ncpy;
+       return ncpy;
 }
 
-static inline int copy_out_page(const char __user *buf, struct page *page,
-                               unsigned offset, unsigned count, int zeroing)
+static inline int fuse_copy_page(struct fuse_copy_state *cs, struct page *page,
+                                unsigned offset, unsigned count, int zeroing)
 {
-       int err = 0;
-       char *tmpbuf = kmap(page);
-       if (count < PAGE_SIZE && zeroing)
-               memset(tmpbuf, 0, PAGE_SIZE);
-       if (count)
-               err = copy_from_user(tmpbuf + offset, buf, count);
-       flush_dcache_page(page);
-       kunmap(page);
-       return err;
+       if (page && zeroing && count < PAGE_SIZE) {
+               void *mapaddr = kmap_atomic(page, KM_USER1);
+               memset(mapaddr, 0, PAGE_SIZE);
+               kunmap_atomic(mapaddr, KM_USER1);
+       }
+       while (count) {
+               int err;
+               if (!cs->len && (err = fuse_copy_fill(cs)))
+                       return err;
+               if (page) {
+                       void *mapaddr = kmap_atomic(page, KM_USER1);
+                       void *buf = mapaddr + offset;
+                       offset += fuse_copy_do(cs, &buf, &count);
+                       kunmap_atomic(mapaddr, KM_USER1);
+               } else
+                       offset += fuse_copy_do(cs, NULL, &count);
+       }
+       if (page && !cs->write)
+               flush_dcache_page(page);
+       return 0;
 }
 
-static int copy_out_pages(struct fuse_req *req, size_t nbytes,
-                         const char __user *buf)
+static int fuse_copy_pages(struct fuse_copy_state *cs, unsigned nbytes,
+                          int zeroing)
 {
        unsigned i;
+       struct fuse_req *req = cs->req;
        unsigned offset = req->page_offset;
-       unsigned zeroing = req->out.page_zeroing;
        unsigned count = min(nbytes, (unsigned) PAGE_SIZE - offset);
 
-       for (i = 0; i < req->num_pages && (zeroing || nbytes); i++) {
+       for (i = 0; i < req->num_pages && nbytes; i++) {
                struct page *page = req->pages[i];
-               if (page && copy_out_page(buf, page, offset, count, zeroing))
-                   return -EFAULT;
+               int err = fuse_copy_page(cs, page, offset, count, zeroing);
+               if (err)
+                       return err;
+
                nbytes -= count;
-               buf += count;
                count = min(nbytes, (unsigned) PAGE_SIZE);
                offset = 0;
        }
        return 0;
 }
 
-static inline int copy_out_one(struct fuse_req *req, struct fuse_out_arg *arg,
-                              int islast, const char __user *buf, size_t nbytes)
+static int fuse_copy_one(struct fuse_copy_state *cs, void *val, unsigned size)
 {
-       if (nbytes < arg->size) {
-               if (!islast || !req->out.argvar) {
-                       printk("fuse_dev_write: write is short\n");
-                       return -EINVAL;
-               }
-               arg->size = nbytes;
+       while (size) {
+               int err;
+               if (!cs->len && (err = fuse_copy_fill(cs)))
+                       return err;
+               fuse_copy_do(cs, &val, &size);
        }
-       if (islast && req->out.argpages)
-               return copy_out_pages(req, arg->size, buf);
-       else if (arg->size && copy_from_user(arg->value, buf, arg->size))
-               return -EFAULT;
-       else
-               return 0;
+       return 0;
 }
 
-static int copy_out_args(struct fuse_req *req, const char __user *buf,
-                        size_t nbytes)
+static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs,
+                         unsigned argpages, struct fuse_arg *args,
+                         int zeroing)
 {
-       struct fuse_out *out = &req->out;
-       int i;
-
-       buf += sizeof(struct fuse_out_header);
-       nbytes -= sizeof(struct fuse_out_header);
+       int err = 0;
+       unsigned i;
 
-       if (!out->h.error) {
-               for (i = 0; i < out->numargs; i++) {
-                       struct fuse_out_arg *arg = &out->args[i];
-                       int islast = (i == out->numargs - 1);
-                       int err = copy_out_one(req, arg, islast, buf, nbytes);
-                       if (err)
-                               return err;
-                       buf += arg->size;
-                       nbytes -= arg->size;
-               }
-       }
-       if (nbytes != 0) {
-               printk("fuse_dev_write: write is long\n");
-               return -EINVAL;
+       for (i = 0; !err && i < numargs; i++)  {
+               struct fuse_arg *arg = &args[i];
+               if (i == numargs - 1 && argpages)
+                       err = fuse_copy_pages(cs, arg->size, zeroing);
+               else
+                       err = fuse_copy_one(cs, arg->value, arg->size);
        }
+       return err;
+}
 
-       return 0;
+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 inline int copy_out_header(struct fuse_out_header *oh,
-                                 const char __user *buf, size_t nbytes)
+static void request_wait(struct fuse_conn *fc)
 {
-       if (nbytes < sizeof(struct fuse_out_header)) {
-               printk("fuse_dev_write: write is short\n");
-               return -EINVAL;
-       }
-       if (copy_from_user(oh, buf, sizeof(struct fuse_out_header)))
-               return -EFAULT;
+       DECLARE_WAITQUEUE(wait, current);
 
-       return 0;
+       add_wait_queue_exclusive(&fc->waitq, &wait);
+       while (fc->sb && list_empty(&fc->pending)) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (signal_pending(current))
+                       break;
+
+               spin_unlock(&fuse_lock);
+               schedule();
+               spin_lock(&fuse_lock);
+       }
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&fc->waitq, &wait);
 }
 
-static int fuse_invalidate(struct fuse_conn *fc, struct fuse_user_header *uh)
+static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
+                             unsigned long nr_segs, loff_t *off)
 {
        int err;
-       down(&fc->sb_sem);
+       struct fuse_conn *fc;
+       struct fuse_req *req;
+       struct fuse_in *in;
+       struct fuse_copy_state cs;
+       unsigned nbytes;
+       unsigned reqsize;
+
+       spin_lock(&fuse_lock);
+       fc = file->private_data;
+       err = -EPERM;
+       if (!fc)
+               goto err_unlock;
+       request_wait(fc);
        err = -ENODEV;
-       if (fc->sb) {
-               struct inode *inode;
-#ifdef KERNEL_2_6
-               inode = fuse_ilookup(fc->sb, uh->nodeid);
-#else
-               inode = fuse_ilookup(fc->sb, uh->ino, uh->nodeid);
-#endif
+       if (!fc->sb)
+               goto err_unlock;
+       err = -EINTR;
+       if (list_empty(&fc->pending))
+               goto err_unlock;
+
+       req = list_entry(fc->pending.next, struct fuse_req, list);
+       list_del_init(&req->list);
+       spin_unlock(&fuse_lock);
+
+       in = &req->in;
+       reqsize = sizeof(struct fuse_in_header);
+       reqsize += len_args(in->numargs, (struct fuse_arg *) in->args);
+       nbytes = fuse_copy_init(&cs, 1, req, iov, nr_segs);
+       err = -EINVAL;
+       if (nbytes >= reqsize) {
+               err = fuse_copy_one(&cs, &in->h, sizeof(in->h));
+               if (!err)
+                       err = fuse_copy_args(&cs, in->numargs, in->argpages,
+                                            (struct fuse_arg *) in->args, 0);
+       }
+       fuse_copy_finish(&cs);
+
+       spin_lock(&fuse_lock);
+       req->locked = 0;
+       if (!err && req->interrupted)
                err = -ENOENT;
-               if (inode) {
-                       fuse_sync_inode(inode);
-#ifdef KERNEL_2_6
-                       invalidate_inode_pages(inode->i_mapping);
-#else
-                       invalidate_inode_pages(inode);
-#endif
-                       iput(inode);
-                       err = 0;
-               }
+       if (err) {
+               if (!req->interrupted)
+                       req->out.h.error = -EIO;
+               request_end(fc, req);
+               return err;
        }
-       up(&fc->sb_sem);
+       if (!req->isreply)
+               request_end(fc, req);
+       else {
+               req->sent = 1;
+               list_add_tail(&req->list, &fc->processing);
+               spin_unlock(&fuse_lock);
+       }
+       return reqsize;
 
+ err_unlock:
+       spin_unlock(&fuse_lock);
        return err;
 }
 
-static int fuse_user_request(struct fuse_conn *fc, const char __user *buf,
-                            size_t nbytes)
+static ssize_t fuse_dev_read(struct file *file, char __user *buf,
+                            size_t nbytes, loff_t *off)
 {
-       struct fuse_user_header uh;
-       int err;
+       struct iovec iov;
+       iov.iov_len = nbytes;
+       iov.iov_base = buf;
+       return fuse_dev_readv(file, &iov, 1, off);
+}
 
-       if (nbytes < sizeof(struct fuse_user_header)) {
-               printk("fuse_dev_write: write is short\n");
-               return -EINVAL;
+static struct fuse_req *request_find(struct fuse_conn *fc, unsigned unique)
+{
+       struct list_head *entry;
+
+       list_for_each(entry, &fc->processing) {
+               struct fuse_req *req;
+               req = list_entry(entry, struct fuse_req, list);
+               if (req->in.h.unique == unique)
+                       return req;
        }
+       return NULL;
+}
+
+/* fget() needs to be done in this context */
+static void process_getdir(struct fuse_req *req)
+{
+       struct fuse_getdir_out_i *arg = req->out.args[0].value;
+       arg->file = fget(arg->fd);
+}
 
-       if (copy_from_user(&uh, buf, sizeof(struct fuse_user_header)))
-               return -EFAULT;
+static int copy_out_args(struct fuse_copy_state *cs, struct fuse_out *out,
+                        unsigned nbytes)
+{
+       unsigned reqsize = sizeof(struct fuse_out_header);
+
+       if (out->h.error)
+               return nbytes != reqsize ? -EINVAL : 0;
 
-       switch (uh.opcode) {
-       case FUSE_INVALIDATE:
-               err = fuse_invalidate(fc, &uh);
-               break;
+       reqsize += len_args(out->numargs, out->args);
 
-       default:
-               err = -ENOSYS;
+       if (reqsize < nbytes || (reqsize > nbytes && !out->argvar))
+               return -EINVAL;
+       else if (reqsize > nbytes) {
+               struct fuse_arg *lastarg = &out->args[out->numargs-1];
+               unsigned diffsize = reqsize - nbytes;
+               if (diffsize > lastarg->size)
+                       return -EINVAL;
+               lastarg->size -= diffsize;
        }
-       return err;
+       return fuse_copy_args(cs, out->numargs, out->argpages, out->args,
+                             out->page_zeroing);
 }
 
-static ssize_t fuse_dev_write(struct file *file, const char __user *buf,
-                             size_t nbytes, loff_t *off)
+static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov,
+                              unsigned long nr_segs, loff_t *off)
 {
        int err;
-       struct fuse_conn *fc = fuse_get_conn(file);
+       unsigned nbytes;
        struct fuse_req *req;
        struct fuse_out_header oh;
-
+       struct fuse_copy_state cs;
+       struct fuse_conn *fc = fuse_get_conn(file);
        if (!fc)
                return -ENODEV;
 
-       err = copy_out_header(&oh, buf, nbytes);
-       if (err)
-               return err;
-
-       if (!oh.unique) {
-               err = fuse_user_request(fc, buf, nbytes);
-               goto out;
-       }
+       nbytes = fuse_copy_init(&cs, 0, NULL, iov, nr_segs);
+       if (nbytes < sizeof(struct fuse_out_header))
+               return -EINVAL;
 
-        if (oh.error <= -1000 || oh.error > 0) {
-                printk("fuse_dev_write: bad error value\n");
-                return -EINVAL;
-        }
+       err = fuse_copy_one(&cs, &oh, sizeof(oh));
+       if (err)
+               goto err_finish;
+       err = -EINVAL;
+       if (!oh.unique || oh.error <= -1000 || oh.error > 0)
+               goto err_finish;
 
        spin_lock(&fuse_lock);
        req = request_find(fc, oh.unique);
-       if (req != NULL)
-               list_del_init(&req->list);
-       spin_unlock(&fuse_lock);
+       err = -ENOENT;
        if (!req)
-               return -ENOENT;
+               goto err_unlock;
 
+       list_del_init(&req->list);
        req->out.h = oh;
-       err = copy_out_args(req, buf, nbytes);
+       req->locked = 1;
+       cs.req = req;
+       spin_unlock(&fuse_lock);
+
+       err = copy_out_args(&cs, &req->out, nbytes);
+       fuse_copy_finish(&cs);
 
        spin_lock(&fuse_lock);
-       if (err)
-               req->out.h.error = -EPROTO;
-       else {
-               /* fget() needs to be done in this context */
-               if (req->in.h.opcode == FUSE_GETDIR && !oh.error)
+       req->locked = 0;
+       if (!err) {
+               if (req->interrupted)
+                       err = -ENOENT;
+               else if (req->in.h.opcode == FUSE_GETDIR && !oh.error)
                        process_getdir(req);
-       }
-       req->finished = 1;
-       /* Unlocks fuse_lock: */
+       } else if (!req->interrupted)
+               req->out.h.error = -EIO;
        request_end(fc, req);
 
- out:
-       if (!err)
-               return nbytes;
-       else
-               return err;
+       return err ? err : nbytes;
+
+ err_unlock:
+       spin_unlock(&fuse_lock);
+ err_finish:
+       fuse_copy_finish(&cs);
+       return err;
+}
+
+static ssize_t fuse_dev_write(struct file *file, const char __user *buf,
+                             size_t nbytes, loff_t *off)
+{
+       struct iovec iov;
+       iov.iov_len = nbytes;
+       iov.iov_base = (char __user *) buf;
+       return fuse_dev_writev(file, &iov, 1, off);
 }
 
 static unsigned fuse_dev_poll(struct file *file, poll_table *wait)
@@ -574,17 +709,9 @@ static void end_requests(struct fuse_conn *fc, struct list_head *head)
                struct fuse_req *req;
                req = list_entry(head->next, struct fuse_req, list);
                list_del_init(&req->list);
-               if (req->isreply) {
-                       req->out.h.error = -ECONNABORTED;
-                       req->finished = 1;
-                       /* Unlocks fuse_lock: */
-                       request_end(fc, req);
-                       spin_lock(&fuse_lock);
-               } else {
-                       spin_unlock(&fuse_lock);
-                       fuse_put_request(fc, req);
-                       spin_lock(&fuse_lock);
-               }
+               req->out.h.error = -ECONNABORTED;
+               request_end(fc, req);
+               spin_lock(&fuse_lock);
        }
 }
 
@@ -607,7 +734,9 @@ static int fuse_dev_release(struct inode *inode, struct file *file)
 struct file_operations fuse_dev_operations = {
        .owner          = THIS_MODULE,
        .read           = fuse_dev_read,
+       .readv          = fuse_dev_readv,
        .write          = fuse_dev_write,
+       .writev         = fuse_dev_writev,
        .poll           = fuse_dev_poll,
        .release        = fuse_dev_release,
 };
@@ -711,7 +840,6 @@ int __init fuse_dev_init(void)
        if (!fuse_req_cachep)
                goto out_version_clean;
 
-
        err = misc_register(&fuse_miscdevice);
        if (err)
                goto out_cache_clean;
index 1c76c5380bf35dd3f6ad72c8a69bec89880c4ad9..992c87df01f69d0ea601a237464b2cd5e0df4369 100644 (file)
@@ -1,9 +1,9 @@
 /*
-    FUSE: Filesystem in Userspace
-    Copyright (C) 2001-2004  Miklos Szeredi <miklos@szeredi.hu>
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2004  Miklos Szeredi <miklos@szeredi.hu>
 
-    This program can be distributed under the terms of the GNU GPL.
-    See the file COPYING.
+  This program can be distributed under the terms of the GNU GPL.
+  See the file COPYING.
 */
 
 #include "fuse_i.h"
@@ -81,7 +81,6 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
                                   new_decode_dev(attr->rdev));
        } else {
                /* Don't let user create weird files */
-               printk("fuse_init_inode: bad file type: %o\n", inode->i_mode);
                inode->i_mode = S_IFREG;
                inode->i_op = &fuse_file_inode_operations;
                fuse_init_file_inode(inode);
@@ -120,8 +119,7 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
                inode->i_data.backing_dev_info = &fc->bdi;
                fuse_init_inode(inode, attr);
                unlock_new_inode(inode);
-       } else if (inode->i_generation != generation)
-               printk("fuse_iget: bad generation for node %lu\n", nodeid);
+       }
 
        change_attributes(inode, attr);
        inode->i_version = version;
@@ -155,8 +153,7 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
                inode->u.generic_ip = inode;
                inode->i_generation = generation;
                fuse_init_inode(inode, attr);
-       } else if (inode->i_generation != generation)
-               printk("fuse_iget: bad generation for node %lu\n", nodeid);
+       }
 
        change_attributes(inode, attr);
        inode->i_version = version;
@@ -174,9 +171,9 @@ struct inode *fuse_ilookup(struct super_block *sb, ino_t ino, unsigned long node
 }
 #endif
 
-static int fuse_send_lookup(struct fuse_conn *fc, struct fuse_req *req,
-                           struct inode *dir, struct dentry *entry,
-                           struct fuse_entry_out *outarg, int *version)
+static void fuse_lookup_init(struct fuse_req *req, struct inode *dir,
+                            struct dentry *entry,
+                            struct fuse_entry_out *outarg)
 {
        req->in.h.opcode = FUSE_LOOKUP;
        req->in.h.nodeid = get_node_id(dir);
@@ -186,27 +183,6 @@ static int fuse_send_lookup(struct fuse_conn *fc, struct fuse_req *req,
        req->out.numargs = 1;
        req->out.args[0].size = sizeof(struct fuse_entry_out);
        req->out.args[0].value = outarg;
-       request_send(fc, req);
-       *version = req->out.h.unique;
-       return req->out.h.error;
-}
-
-static int fuse_do_lookup(struct inode *dir, struct dentry *entry,
-                         struct fuse_entry_out *outarg, int *version)
-{
-       struct fuse_conn *fc = get_fuse_conn(dir);
-       struct fuse_req *req;
-       int err;
-
-       if (entry->d_name.len > FUSE_NAME_MAX)
-               return -ENAMETOOLONG;
-       req = fuse_get_request(fc);
-       if (!req)
-               return -ERESTARTSYS;
-
-       err = fuse_send_lookup(fc, req, dir, entry, outarg, version);
-       fuse_put_request(fc, req);
-       return err;
 }
 
 static inline unsigned long time_to_jiffies(unsigned long sec,
@@ -233,9 +209,12 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry,
                return -ENAMETOOLONG;
        req = fuse_get_request(fc);
        if (!req)
-               return -ERESTARTSYS;
+               return -ERESTARTNOINTR;
 
-       err = fuse_send_lookup(fc, req, dir, entry, &outarg, &version);
+       fuse_lookup_init(req, dir, entry, &outarg);
+       request_send(fc, req);
+       version = req->out.h.unique;
+       err = req->out.h.error;
        if (!err) {
                inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
                                  &outarg.attr, version);
@@ -284,8 +263,7 @@ static int lookup_new_entry(struct fuse_conn *fc, struct fuse_req *req,
        /* Don't allow userspace to do really stupid things... */
        if ((inode->i_mode ^ mode) & S_IFMT) {
                iput(inode);
-               printk("fuse_mknod: inode has wrong type\n");
-               return -EPROTO;
+               return -EIO;
        }
 
        entry->d_time = time_to_jiffies(outarg->entry_valid,
@@ -310,7 +288,7 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
        int err;
 
        if (!req)
-               return -ERESTARTSYS;
+               return -ERESTARTNOINTR;
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.mode = mode;
@@ -350,7 +328,7 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode)
        int err;
 
        if (!req)
-               return -ERESTARTSYS;
+               return -ERESTARTNOINTR;
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.mode = mode;
@@ -388,7 +366,7 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry,
 
        req = fuse_get_request(fc);
        if (!req)
-               return -ERESTARTSYS;
+               return -ERESTARTNOINTR;
 
        req->in.h.opcode = FUSE_SYMLINK;
        req->in.h.nodeid = get_node_id(dir);
@@ -417,7 +395,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
        int err;
 
        if (!req)
-               return -ERESTARTSYS;
+               return -ERESTARTNOINTR;
 
        req->in.h.opcode = FUSE_UNLINK;
        req->in.h.nodeid = get_node_id(dir);
@@ -447,7 +425,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
        int err;
 
        if (!req)
-               return -ERESTARTSYS;
+               return -ERESTARTNOINTR;
 
        req->in.h.opcode = FUSE_RMDIR;
        req->in.h.nodeid = get_node_id(dir);
@@ -473,7 +451,7 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
        int err;
 
        if (!req)
-               return -ERESTARTSYS;
+               return -ERESTARTNOINTR;
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.newdir = get_node_id(newdir);
@@ -508,7 +486,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
        int err;
 
        if (!req)
-               return -ERESTARTSYS;
+               return -ERESTARTNOINTR;
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.newdir = get_node_id(newdir);
@@ -542,7 +520,7 @@ int fuse_do_getattr(struct inode *inode)
        int err;
 
        if (!req)
-               return -ERESTARTSYS;
+               return -ERESTARTNOINTR;
 
        req->in.h.opcode = FUSE_GETATTR;
        req->in.h.nodeid = get_node_id(inode);
@@ -604,7 +582,7 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd)
 #ifdef KERNEL_2_6_10_PLUS
                                err = generic_permission(inode, mask, NULL);
 #else
-                               err = vfs_permission(inode, mask);
+                       err = vfs_permission(inode, mask);
 #endif
                }
 
@@ -636,10 +614,8 @@ 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) {
-                       printk("fuse_readdir: name too long\n");
-                       return -EPROTO;
-               }
+               if (dirent->namelen > NAME_MAX)
+                       return -EIO;
                if (reclen > nbytes)
                        break;
 
@@ -659,15 +635,12 @@ static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
 static int fuse_checkdir(struct file *cfile, struct file *file)
 {
        struct inode *inode;
-       if (!cfile) {
-               printk("fuse_getdir: invalid file\n");
-               return -EPROTO;
-       }
+       if (!cfile)
+               return -EIO;
        inode = cfile->f_dentry->d_inode;
        if (!S_ISREG(inode->i_mode)) {
-               printk("fuse_getdir: not a regular file\n");
                fput(cfile);
-               return -EPROTO;
+               return -EIO;
        }
 
        file->private_data = cfile;
@@ -683,7 +656,7 @@ static int fuse_getdir(struct file *file)
        int err;
 
        if (!req)
-               return -ERESTARTSYS;
+               return -ERESTARTNOINTR;
 
        req->in.h.opcode = FUSE_GETDIR;
        req->in.h.nodeid = get_node_id(inode);
@@ -717,9 +690,7 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
                return -ENOMEM;
 
        ret = kernel_read(cfile, file->f_pos, buf, PAGE_SIZE);
-       if (ret < 0)
-               printk("fuse_readdir: failed to read container file\n");
-       else
+       if (ret > 0)
                ret = parse_dirfile(buf, ret, file, dstbuf, filldir);
 
        free_page((unsigned long) buf);
@@ -734,7 +705,7 @@ static char *read_link(struct dentry *dentry)
        char *link;
 
        if (!req)
-               return ERR_PTR(-ERESTARTSYS);
+               return ERR_PTR(-ERESTARTNOINTR);
 
        link = (char *) __get_free_page(GFP_KERNEL);
        if (!link) {
@@ -866,10 +837,7 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
 
        req = fuse_get_request(fc);
        if (!req)
-               return -ERESTARTSYS;
-
-       if (is_truncate)
-               down_write(&fi->write_sem);
+               return -ERESTARTNOINTR;
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.valid = iattr_to_fattr(attr, &inarg.attr);
@@ -889,15 +857,13 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
                if (is_truncate) {
                        loff_t origsize = i_size_read(inode);
                        i_size_write(inode, outarg.attr.size);
-                       up_write(&fi->write_sem);
                        if (origsize > outarg.attr.size)
                                vmtruncate(inode, outarg.attr.size);
                }
                change_attributes(inode, &outarg.attr);
                fi->i_time = time_to_jiffies(outarg.attr_valid,
                                             outarg.attr_valid_nsec);
-       } else if (is_truncate)
-               up_write(&fi->write_sem);
+       }
 
        return err;
 }
@@ -907,15 +873,22 @@ 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_entry_out outarg;
-               int version;
-               int ret;
+               struct fuse_conn *fc = get_fuse_conn(inode);
+               struct fuse_req *req = fuse_get_request_nonint(fc);
+               if (!req)
+                       return 0;
 
-               ret = fuse_do_lookup(entry->d_parent->d_inode, entry, &outarg,
-                                    &version);
-               if (ret)
+               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))
@@ -966,8 +939,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
            (alias = d_find_alias(inode)) != NULL) {
                dput(alias);
                iput(inode);
-               printk("fuse: cannot assign an existing directory\n");
-               return ERR_PTR(-EPROTO);
+               return ERR_PTR(-EIO);
        }
 
        d_add(entry, inode);
@@ -1019,7 +991,7 @@ static int fuse_setxattr(struct dentry *entry, const char *name,
 
        req = fuse_get_request(fc);
        if (!req)
-               return -ERESTARTSYS;
+               return -ERESTARTNOINTR;
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.size = size;
@@ -1058,7 +1030,7 @@ static ssize_t fuse_getxattr(struct dentry *entry, const char *name,
 
        req = fuse_get_request(fc);
        if (!req)
-               return -ERESTARTSYS;
+               return -ERESTARTNOINTR;
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.size = size;
@@ -1107,7 +1079,7 @@ static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
 
        req = fuse_get_request(fc);
        if (!req)
-               return -ERESTARTSYS;
+               return -ERESTARTNOINTR;
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.size = size;
@@ -1152,7 +1124,7 @@ static int fuse_removexattr(struct dentry *entry, const char *name)
 
        req = fuse_get_request(fc);
        if (!req)
-               return -ERESTARTSYS;
+               return -ERESTARTNOINTR;
 
        req->in.h.opcode = FUSE_REMOVEXATTR;
        req->in.h.nodeid = get_node_id(inode);
index 738f1f030518066b2e688e053cd9806cbac8a9aa..6758c2bb84e9c5e2155ddbd4e751c7d9239024c6 100644 (file)
@@ -1,9 +1,9 @@
 /*
-    FUSE: Filesystem in Userspace
-    Copyright (C) 2001-2004  Miklos Szeredi <miklos@szeredi.hu>
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2004  Miklos Szeredi <miklos@szeredi.hu>
 
-    This program can be distributed under the terms of the GNU GPL.
-    See the file COPYING.
+  This program can be distributed under the terms of the GNU GPL.
+  See the file COPYING.
 */
 
 #include "fuse_i.h"
 #include <linux/pagemap.h>
 #include <linux/slab.h>
 #include <linux/kernel.h>
-#include <linux/module.h>
-#ifdef KERNEL_2_6
-#include <linux/writeback.h>
-#include <linux/moduleparam.h>
-#endif
-#include <asm/uaccess.h>
 
-static int user_mmap;
-#ifdef KERNEL_2_6
-module_param(user_mmap, int, 0644);
-#else
-MODULE_PARM(user_mmap, "i");
+#ifndef KERNEL_2_6
 #define PageUptodate(page) Page_Uptodate(page)
+#define clear_page_dirty(page) ClearPageDirty(page)
 #endif
-MODULE_PARM_DESC(user_mmap, "Allow non root user to create a shared writable mapping");
-
 static int fuse_open(struct inode *inode, struct file *file)
 {
        struct fuse_conn *fc = get_fuse_conn(inode);
@@ -48,6 +37,7 @@ 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);
@@ -91,7 +81,6 @@ static int fuse_open(struct inode *inode, struct file *file)
        else {
                ff->fh = outarg.fh;
                file->private_data = ff;
-               INIT_LIST_HEAD(&ff->ff_list);
        }
 
  out_put_request:
@@ -101,49 +90,23 @@ static int fuse_open(struct inode *inode, struct file *file)
        return err;
 }
 
-void fuse_sync_inode(struct inode *inode)
-{
-#ifdef KERNEL_2_6
-       filemap_fdatawrite(inode->i_mapping);
-       filemap_fdatawait(inode->i_mapping);
-#else
-#ifndef NO_MM
-       filemap_fdatasync(inode->i_mapping);
-       filemap_fdatawait(inode->i_mapping);
-#endif
-#endif
-}
-
 static int fuse_release(struct inode *inode, struct file *file)
 {
        struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_file *ff = file->private_data;
        struct fuse_req *req = ff->release_req;
-       struct fuse_release_in inarg;
-
-       down(&inode->i_sem);
-       if (file->f_mode & FMODE_WRITE)
-               fuse_sync_inode(inode);
-
-       if (!list_empty(&ff->ff_list)) {
-               struct fuse_inode *fi = get_fuse_inode(inode);
-               down_write(&fi->write_sem);
-               list_del(&ff->ff_list);
-               up_write(&fi->write_sem);
-       }
+       struct fuse_release_in *inarg = &req->misc.release_in;
 
-       memset(&inarg, 0, sizeof(inarg));
-       inarg.fh = ff->fh;
-       inarg.flags = file->f_flags & ~O_EXCL;
+       inarg->fh = ff->fh;
+       inarg->flags = file->f_flags & ~O_EXCL;
        req->in.h.opcode = FUSE_RELEASE;
        req->in.h.nodeid = get_node_id(inode);
        req->in.numargs = 1;
-       req->in.args[0].size = sizeof(inarg);
-       req->in.args[0].value = &inarg;
-       request_send(fc, req);
+       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);
        kfree(ff);
-       up(&inode->i_sem);
 
        /* Return value is ignored by VFS */
        return 0;
@@ -154,14 +117,17 @@ static int fuse_flush(struct file *file)
        struct inode *inode = file->f_dentry->d_inode;
        struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_file *ff = file->private_data;
-       struct fuse_req *req = ff->release_req;
+       struct fuse_req *req;
        struct fuse_flush_in inarg;
        int err;
 
        if (fc->no_flush)
                return 0;
 
-       down(&inode->i_sem);
+       req = fuse_get_request_nonint(fc);
+       if (!req)
+               return -EINTR;
+
        memset(&inarg, 0, sizeof(inarg));
        inarg.fh = ff->fh;
        req->in.h.opcode = FUSE_FLUSH;
@@ -169,10 +135,9 @@ static int fuse_flush(struct file *file)
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(inarg);
        req->in.args[0].value = &inarg;
-       request_send(fc, req);
+       request_send_nonint(fc, req, 0);
        err = req->out.h.error;
-       fuse_reset_request(req);
-       up(&inode->i_sem);
+       fuse_put_request(fc, req);
        if (err == -ENOSYS) {
                fc->no_flush = 1;
                err = 0;
@@ -183,7 +148,6 @@ static int fuse_flush(struct file *file)
 static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
 {
        struct inode *inode = de->d_inode;
-       struct fuse_inode *fi = get_fuse_inode(inode);
        struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_file *ff = file->private_data;
        struct fuse_req *req;
@@ -197,11 +161,6 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
        if (!req)
                return -ERESTARTSYS;
 
-       /* Make sure all writes to this inode are completed before
-          issuing the FSYNC request */
-       down_write(&fi->write_sem);
-       up_write(&fi->write_sem);
-
        memset(&inarg, 0, sizeof(inarg));
        inarg.fh = ff->fh;
        inarg.datasync = datasync;
@@ -212,47 +171,52 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
        req->in.args[0].value = &inarg;
        request_send(fc, req);
        err = req->out.h.error;
+       fuse_put_request(fc, req);
        if (err == -ENOSYS) {
                fc->no_fsync = 1;
                err = 0;
        }
-       fuse_put_request(fc, req);
        return err;
 }
 
-static void fuse_read_init(struct fuse_req *req, struct file *file,
-                          struct inode *inode, loff_t pos, size_t count)
+static ssize_t fuse_send_read(struct fuse_req *req, struct file *file,
+                             struct inode *inode, loff_t pos,  size_t count)
 {
+       struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_file *ff = file->private_data;
-       struct fuse_read_in *inarg = &req->misc.read_in;
+       struct fuse_read_in inarg;
 
-       inarg->fh = ff->fh;
-       inarg->offset = pos;
-       inarg->size = count;
+       memset(&inarg, 0, sizeof(struct fuse_read_in));
+       inarg.fh = ff->fh;
+       inarg.offset = pos;
+       inarg.size = count;
        req->in.h.opcode = FUSE_READ;
        req->in.h.nodeid = get_node_id(inode);
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(struct fuse_read_in);
-       req->in.args[0].value = inarg;
+       req->in.args[0].value = &inarg;
        req->out.argpages = 1;
        req->out.argvar = 1;
        req->out.numargs = 1;
        req->out.args[0].size = count;
+       request_send_nonint(fc, req, 0);
+       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);
-       struct fuse_req *req = fuse_get_request_nonint(fc);
        loff_t pos = (loff_t) page->index << PAGE_CACHE_SHIFT;
-       int err;
+       struct fuse_req *req = fuse_get_request_nonint(fc);
+       if (!req)
+               return -EINTR;
 
-       fuse_read_init(req, file, inode, pos, PAGE_CACHE_SIZE);
        req->out.page_zeroing = 1;
        req->num_pages = 1;
        req->pages[0] = page;
-       request_send(fc, req);
+       fuse_send_read(req, file, inode, pos, PAGE_CACHE_SIZE);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (!err)
@@ -262,27 +226,21 @@ static int fuse_readpage(struct file *file, struct page *page)
 }
 
 #ifdef KERNEL_2_6
-static void read_pages_end(struct fuse_conn *fc, struct fuse_req *req)
+static int fuse_send_readpages(struct fuse_req *req, struct file *file,
+                              struct inode *inode)
 {
+       loff_t pos = (loff_t) req->pages[0]->index << PAGE_CACHE_SHIFT;
+       size_t count = req->num_pages << PAGE_CACHE_SHIFT;
        unsigned i;
+       req->out.page_zeroing = 1;
+       fuse_send_read(req, file, inode, pos, count);
        for (i = 0; i < req->num_pages; i++) {
                struct page *page = req->pages[i];
                if (!req->out.h.error)
                        SetPageUptodate(page);
                unlock_page(page);
        }
-       fuse_put_request(fc, req);
-}
-
-static void fuse_send_readpages(struct fuse_req *req, struct file *file,
-                               struct inode *inode)
-{
-       struct fuse_conn *fc = get_fuse_conn(inode);
-       loff_t pos = (loff_t) req->pages[0]->index << PAGE_CACHE_SHIFT;
-       size_t count = req->num_pages << PAGE_CACHE_SHIFT;
-       fuse_read_init(req, file, inode, pos, count);
-       req->out.page_zeroing = 1;
-       request_send_async(fc, req, read_pages_end);
+       return req->out.h.error;
 }
 
 struct fuse_readpages_data {
@@ -302,9 +260,11 @@ static int fuse_readpages_fill(void *_data, struct page *page)
            (req->num_pages == FUSE_MAX_PAGES_PER_REQ ||
             (req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_read ||
             req->pages[req->num_pages - 1]->index + 1 != page->index)) {
-               struct fuse_conn *fc = get_fuse_conn(page->mapping->host);
-               fuse_send_readpages(req, data->file, inode);
-               data->req = req = fuse_get_request_nonint(fc);
+               int err = fuse_send_readpages(req, data->file, inode);
+               if (err)
+                       return err;
+
+               fuse_reset_request(req);
        }
        req->pages[req->num_pages] = page;
        req->num_pages ++;
@@ -317,18 +277,19 @@ static int fuse_readpages(struct file *file, struct address_space *mapping,
        struct inode *inode = mapping->host;
        struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_readpages_data data;
+       int err;
 
-       data.req = fuse_get_request_nonint(fc);
        data.file = file;
        data.inode = inode;
+       data.req = fuse_get_request_nonint(fc);
+       if (!data.req)
+               return -EINTR;
 
-       read_cache_pages(mapping, pages, fuse_readpages_fill, &data);
-       if (data.req->num_pages)
+       err = read_cache_pages(mapping, pages, fuse_readpages_fill, &data);
+       if (!err && data.req->num_pages)
                fuse_send_readpages(data.req, file, inode);
-       else
-               fuse_put_request(fc, data.req);
-
-       return 0;
+       fuse_put_request(fc, data.req);
+       return err;
 }
 #else /* KERNEL_2_6 */
 #define FUSE_BLOCK_SHIFT 16
@@ -356,15 +317,14 @@ static int fuse_is_block_uptodate(struct inode *inode, unsigned start,
        return 1;
 }
 
-static void fuse_file_read_block(struct fuse_req *req, struct file *file,
-                                struct inode *inode, unsigned start,
-                                unsigned end)
+static int fuse_file_read_block(struct fuse_req *req, struct file *file,
+                               struct inode *inode, unsigned start,
+                               unsigned end)
 {
-       struct fuse_conn *fc = get_fuse_conn(inode);
        loff_t pos;
        size_t count;
        int index;
-       int err = 1;
+       int err = -EBUSY;
        int i;
 
        for (index = start; index < end; index++) {
@@ -380,8 +340,7 @@ static void fuse_file_read_block(struct fuse_req *req, struct file *file,
        }
        pos = (loff_t) start << PAGE_CACHE_SHIFT;
        count = req->num_pages << PAGE_CACHE_SHIFT;
-       fuse_read_init(req, file, inode, pos, count);
-       request_send(fc, req);
+       fuse_send_read(req, file, inode, pos, count);
        err = req->out.h.error;
  out:
        for (i = 0; i < req->num_pages; i++) {
@@ -393,6 +352,7 @@ static void fuse_file_read_block(struct fuse_req *req, struct file *file,
                        page_cache_release(page);
                }
        }
+       return err;
 }
 
 static int fuse_file_bigread(struct file *file, struct inode *inode,
@@ -420,7 +380,9 @@ static int fuse_file_bigread(struct file *file, struct inode *inode,
                nexti = starti + (FUSE_BLOCK_SIZE >> PAGE_CACHE_SHIFT);
                nexti = min(nexti, endi);
                if (!fuse_is_block_uptodate(inode, starti, nexti)) {
-                       fuse_file_read_block(req, file, inode, starti, nexti);
+                       if (fuse_file_read_block(req, file, inode, starti, nexti))
+                               break;
+
                        fuse_reset_request(req);
                }
        }
@@ -429,159 +391,32 @@ static int fuse_file_bigread(struct file *file, struct inode *inode,
 }
 #endif /* KERNEL_2_6 */
 
-static void fuse_write_init(struct fuse_req *req, struct fuse_file *ff,
-                           struct inode *inode, loff_t pos, size_t count,
-                           int iswritepage)
+static ssize_t fuse_send_write(struct fuse_req *req, struct file *file,
+                              struct inode *inode, loff_t pos, size_t count)
 {
-       struct fuse_write_in *inarg = &req->misc.write.in;
+       struct fuse_conn *fc = get_fuse_conn(inode);
+       struct fuse_file *ff = file->private_data;
+       struct fuse_write_in inarg;
+       struct fuse_write_out outarg;
 
-       inarg->writepage = iswritepage;
-       inarg->fh = ff->fh;
-       inarg->offset = pos;
-       inarg->size = count;
+       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);
-       if (iswritepage) {
-               req->in.h.uid = 0;
-               req->in.h.gid = 0;
-               req->in.h.pid = 0;
-       }
        req->in.argpages = 1;
        req->in.numargs = 2;
        req->in.args[0].size = sizeof(struct fuse_write_in);
-       req->in.args[0].value = inarg;
+       req->in.args[0].value = &inarg;
        req->in.args[1].size = count;
        req->out.numargs = 1;
        req->out.args[0].size = sizeof(struct fuse_write_out);
-       req->out.args[0].value = &req->misc.write.out;
-}
-
-static int get_write_count(struct inode *inode, struct page *page)
-{
-       unsigned long end_index;
-       loff_t size = i_size_read(inode);
-       int count;
-
-       end_index = size >> PAGE_CACHE_SHIFT;
-       if (page->index < end_index)
-               count = PAGE_CACHE_SIZE;
-       else {
-               count = size & (PAGE_CACHE_SIZE - 1);
-               if (page->index > end_index || count == 0)
-                       return 0;
-       }
-       return count;
-}
-
-static inline struct fuse_file *get_write_file(struct fuse_inode *fi)
-{
-       BUG_ON(list_empty(&fi->write_files));
-       return list_entry(fi->write_files.next, struct fuse_file, ff_list);
-}
-
-#ifdef KERNEL_2_6
-static void write_page_end(struct fuse_conn *fc, struct fuse_req *req)
-{
-       struct page *page = req->pages[0];
-       struct inode *inode = page->mapping->host;
-       struct fuse_inode *fi = get_fuse_inode(inode);
-       if (!req->out.h.error &&
-           req->misc.write.out.size != req->in.args[1].size)
-               req->out.h.error = -EPROTO;
-
-       if (req->out.h.error) {
-               SetPageError(page);
-               if (req->out.h.error == -ENOSPC)
-                       set_bit(AS_ENOSPC, &page->mapping->flags);
-               else
-                       set_bit(AS_EIO, &page->mapping->flags);
-       }
-       up_read(&fi->write_sem);
-       end_page_writeback(page);
-       fuse_put_request(fc, req);
-}
-
-static int fuse_writepage(struct page *page, struct writeback_control *wbc)
-{
-       struct inode *inode = page->mapping->host;
-       struct fuse_conn *fc = get_fuse_conn(inode);
-       struct fuse_inode *fi = get_fuse_inode(inode);
-       struct fuse_req *req;
-       int err;
-
-       err = -EWOULDBLOCK;
-       if (wbc->nonblocking)
-               req = fuse_get_request_nonblock(fc);
-       else
-               req = fuse_get_request_nonint(fc);
-       if (req) {
-               int locked = 1;
-               if (wbc->nonblocking)
-                       locked = down_read_trylock(&fi->write_sem);
-               else
-                       down_read(&fi->write_sem);
-               if (locked) {
-                       unsigned count = get_write_count(inode, page);
-                       loff_t pos = (loff_t) page->index << PAGE_CACHE_SHIFT;
-                       err = 0;
-                       if (count) {
-                               struct fuse_file *ff = get_write_file(fi);
-                               SetPageWriteback(page);
-                               fuse_write_init(req, ff, inode, pos, count, 1);
-                               req->num_pages = 1;
-                               req->pages[0] = page;
-                               request_send_async(fc, req, write_page_end);
-                               goto out;
-                       }
-                       up_read(&fi->write_sem);
-               }
-               fuse_put_request(fc, req);
-       }
-       if (err == -EWOULDBLOCK) {
-#ifdef KERNEL_2_6_6_PLUS
-               redirty_page_for_writepage(wbc, page);
-#else
-               __set_page_dirty_nobuffers(page);
-#endif
-               err = 0;
-       }
- out:
-       unlock_page(page);
-       return err;
-}
-#else
-static int fuse_writepage(struct page *page)
-{
-       int err;
-       unsigned count;
-       struct inode *inode = page->mapping->host;
-       struct fuse_conn *fc = get_fuse_conn(inode);
-       struct fuse_inode *fi = get_fuse_inode(inode);
-       struct fuse_req *req = fuse_get_request_nonint(fc);
-
-       down_read(&fi->write_sem);
-       count = get_write_count(inode, page);
-       err = 0;
-       if (count) {
-               struct fuse_file *ff = get_write_file(fi);
-               loff_t pos = ((loff_t) page->index << PAGE_CACHE_SHIFT);
-
-               fuse_write_init(req, ff, inode, pos, count, 1);
-               req->num_pages = 1;
-               req->pages[0] = page;
-               request_send(fc, req);
-               err = req->out.h.error;
-               if (!err && req->misc.write.out.size != count)
-                       err = -EPROTO;
-       }
-       up_read(&fi->write_sem);
-       fuse_put_request(fc, req);
-       if (err)
-               SetPageError(page);
-       unlock_page(page);
-       return err;
+       req->out.args[0].value = &outarg;
+       request_send_nonint(fc, req, 0);
+       return outarg.size;
 }
-#endif
 
 static int fuse_prepare_write(struct file *file, struct page *page,
                              unsigned offset, unsigned to)
@@ -594,34 +429,29 @@ static int fuse_commit_write(struct file *file, struct page *page,
                             unsigned offset, unsigned to)
 {
        int err;
+       ssize_t nres;
        unsigned count = to - offset;
        struct inode *inode = page->mapping->host;
-       struct fuse_file *ff = file->private_data;
        struct fuse_conn *fc = get_fuse_conn(inode);
-       struct fuse_req *req = fuse_get_request(fc);
        loff_t pos = ((loff_t) page->index << PAGE_CACHE_SHIFT) + offset;
+       struct fuse_req *req = fuse_get_request_nonint(fc);
        if (!req)
-               return -ERESTARTSYS;
+               return -EINTR;
 
-       fuse_write_init(req, ff, inode, pos, count, 0);
        req->num_pages = 1;
        req->pages[0] = page;
        req->page_offset = offset;
-       request_send(fc, req);
+       nres = fuse_send_write(req, file, inode, pos, count);
        err = req->out.h.error;
-       if (!err && req->misc.write.out.size != count)
-               err = -EPROTO;
+       if (!err && nres != count)
+               err = -EIO;
        if (!err) {
                pos += count;
                if (pos > i_size_read(inode))
                        i_size_write(inode, pos);
 
                if (offset == 0 && to == PAGE_CACHE_SIZE) {
-#ifdef KERNEL_2_6
                        clear_page_dirty(page);
-#else
-                       ClearPageDirty(page);
-#endif
                        SetPageUptodate(page);
                }
        }
@@ -635,16 +465,9 @@ static void fuse_release_user_pages(struct fuse_req *req, int write)
 
        for (i = 0; i < req->num_pages; i++) {
                struct page *page = req->pages[i];
-               if (write) {
-#ifdef KERNEL_2_6
+               if (write)
                        set_page_dirty_lock(page);
-#else
-                       lock_page(page);
-                       set_page_dirty(page);
-                       unlock_page(page);
-#endif
-               }
-               page_cache_release(page);
+               put_page(page);
        }
 }
 
@@ -675,7 +498,6 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
 {
        struct inode *inode = file->f_dentry->d_inode;
        struct fuse_conn *fc = get_fuse_conn(inode);
-       struct fuse_file *ff = file->private_data;
        unsigned nmax = write ? fc->max_write : fc->max_read;
        loff_t pos = *ppos;
        ssize_t res = 0;
@@ -695,25 +517,18 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
                tmp = (req->num_pages << PAGE_SHIFT) - req->page_offset;
                nbytes = min(nbytes, tmp);
                if (write)
-                       fuse_write_init(req, ff, inode, pos, nbytes, 0);
+                       nres = fuse_send_write(req, file, inode, pos, nbytes);
                else
-                       fuse_read_init(req, file, inode, pos, nbytes);
-               request_send(fc, req);
+                       nres = fuse_send_read(req, file, inode, pos, nbytes);
                fuse_release_user_pages(req, !write);
                if (req->out.h.error) {
                        if (!res)
                                res = req->out.h.error;
                        break;
+               } else if (nres > nbytes) {
+                       res = -EIO;
+                       break;
                }
-               if (write) {
-                       nres = req->misc.write.out.size;
-                       if (nres > nbytes) {
-                               res = -EPROTO;
-                               break;
-                       }
-               }
-               else
-                       nres = req->out.args[0].size;
                count -= nres;
                res += nres;
                pos += nres;
@@ -756,7 +571,6 @@ static ssize_t fuse_file_read(struct file *file, char __user *buf,
        else
                return generic_file_read(file, buf, count, ppos);
 #endif
-
 }
 
 static ssize_t fuse_file_write(struct file *file, const char __user *buf,
@@ -784,18 +598,11 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
        if (fc->flags & FUSE_DIRECT_IO)
                return -ENODEV;
        else {
-               if ((vma->vm_flags & (VM_WRITE | VM_SHARED)) ==
-                   (VM_WRITE | VM_SHARED)) {
-                       struct fuse_inode *fi = get_fuse_inode(inode);
-                       struct fuse_file *ff = file->private_data;
-
-                       if (!user_mmap && current->uid != 0)
-                               return -EPERM;
-
-                       down_write(&fi->write_sem);
-                       if (list_empty(&ff->ff_list))
-                               list_add(&ff->ff_list, &fi->write_files);
-                       up_write(&fi->write_sem);
+               if ((vma->vm_flags & VM_SHARED)) {
+                       if ((vma->vm_flags & VM_WRITE))
+                               return -ENODEV;
+                       else
+                               vma->vm_flags &= ~VM_MAYWRITE;
                }
                return generic_file_mmap(file, vma);
        }
@@ -816,7 +623,6 @@ static struct file_operations fuse_file_operations = {
 
 static struct address_space_operations fuse_file_aops  = {
        .readpage       = fuse_readpage,
-       .writepage      = fuse_writepage,
        .prepare_write  = fuse_prepare_write,
        .commit_write   = fuse_commit_write,
 #ifdef KERNEL_2_6
index d829f0da94726a3e5e6df3bf39a8999fa672592d..b79d06bbe59dc350fa116245cc9a1c13a97c3203 100644 (file)
@@ -1,9 +1,9 @@
 /*
-    FUSE: Filesystem in Userspace
-    Copyright (C) 2001-2004  Miklos Szeredi <miklos@szeredi.hu>
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2004  Miklos Szeredi <miklos@szeredi.hu>
 
-    This program can be distributed under the terms of the GNU GPL.
-    See the file COPYING.
+  This program can be distributed under the terms of the GNU GPL.
+  See the file COPYING.
 */
 
 #ifdef FUSE_MAINLINE
@@ -37,7 +37,7 @@
 #     define i_size_read(inode) ((inode)->i_size)
 #     define i_size_write(inode, size) do { (inode)->i_size = size; } while(0)
 #  endif
-#endif
+#endif /* KERNEL_2_6 */
 #endif /* FUSE_MAINLINE */
 #include <linux/fs.h>
 #include <linux/wait.h>
 #ifndef __user
 #define __user
 #endif
+#ifndef KERNEL_2_6
+#include <linux/pagemap.h>
+static inline void set_page_dirty_lock(struct page *page)
+{
+       lock_page(page);
+       set_page_dirty(page);
+       unlock_page(page);
+}
+#endif
 /* 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 */
 #define FUSE_MAX_OUTSTANDING 10
 
-
 /** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem
     module will check permissions based on the file mode.  Otherwise no
     permission checking is done in the kernel */
@@ -96,28 +104,17 @@ struct fuse_inode {
        /** The request used for sending the FORGET message */
        struct fuse_req *forget_req;
 
-       /** Semaphore protects the 'write_files' list, and against
-           truncate racing with async writeouts */
-       struct rw_semaphore write_sem;
-
        /** Time in jiffies until the file attributes are valid */
        unsigned long i_time;
-
-       /* List of fuse_files which can provide file handles in
-        * writepage, protected by write_sem */
-       struct list_head write_files;
 };
 
 /** FUSE specific file data */
 struct fuse_file {
        /** Request reserved for flush and release */
        struct fuse_req *release_req;
-       
+
        /** File handle used by userspace */
        unsigned long fh;
-
-       /** Element in fuse_inode->write_files */
-       struct list_head ff_list;
 };
 
 /** One input argument of a request */
@@ -142,7 +139,7 @@ struct fuse_in {
 };
 
 /** One output argument of a request */
-struct fuse_out_arg {
+struct fuse_arg {
        unsigned size;
        void *value;
 };
@@ -155,10 +152,10 @@ struct fuse_out {
        /** Last argument is variable length (can be shorter than
            arg->size) */
        unsigned argvar:1;
-       
+
        /** Last argument is a list of pages to copy data to */
        unsigned argpages:1;
-       
+
        /** Zero partially or not copied pages */
        unsigned page_zeroing:1;
 
@@ -166,30 +163,40 @@ struct fuse_out {
        unsigned numargs;
 
        /** Array of arguments */
-       struct fuse_out_arg args[3];
+       struct fuse_arg args[3];
 };
 
 struct fuse_req;
 struct fuse_conn;
 
-/** Function called on finishing an async request */
-typedef void (*fuse_reqend_t)(struct fuse_conn *, struct fuse_req *);
-
 /**
  * A request to the client
  */
 struct fuse_req {
-       /** The request list */
+       /** This can be on either unused_list, pending or processing
+           lists in fuse_conn */
        struct list_head list;
 
+       /** refcount */
+       atomic_t count;
+
        /** True if the request has reply */
        unsigned isreply:1;
 
-       /* The request is preallocated */
+       /** The request is preallocated */
        unsigned preallocated:1;
 
-       /* The request is finished */
-       unsigned finished;
+       /** The request was interrupted */
+       unsigned interrupted:1;
+
+       /** Data is being copied to/from the request */
+       unsigned locked:1;
+
+       /** Request has been sent to userspace */
+       unsigned sent:1;
+
+       /** The request is finished */
+       unsigned finished:1;
 
        /** The request input */
        struct fuse_in in;
@@ -200,17 +207,10 @@ struct fuse_req {
        /** Used to wake up the task waiting for completion of request*/
        wait_queue_head_t waitq;
 
-       /** Request completion callback */
-       fuse_reqend_t end;
-
        /** Data for asynchronous requests */
        union {
-               struct {
-                       struct fuse_write_in in;
-                       struct fuse_write_out out;
-               } write;
-               struct fuse_read_in read_in;
                struct fuse_forget_in forget_in;
+               struct fuse_release_in release_in;
        } misc;
 
        /** page vector */
@@ -261,9 +261,6 @@ struct fuse_conn {
        /** Controls the maximum number of outstanding requests */
        struct semaphore unused_sem;
 
-       /** Semaphore protecting the super block from going away */
-       struct semaphore sb_sem;
-
        /** The list of unused requests */
        struct list_head unused_list;
 
@@ -418,42 +415,35 @@ void fuse_reset_request(struct fuse_req *req);
 struct fuse_req *fuse_get_request(struct fuse_conn *fc);
 
 /**
- * Reserve a preallocated request, non-interruptible
+ * Reserve a preallocated request, only interruptible by SIGKILL
  */
 struct fuse_req *fuse_get_request_nonint(struct fuse_conn *fc);
 
-/**
- * Reserve a preallocated request, non-blocking
- */
-struct fuse_req *fuse_get_request_nonblock(struct fuse_conn *fc);
-
 /**
  * Free a request
  */
 void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req);
 
 /**
- * Send a request
+ * Send a request (synchronous, interruptible)
  */
 void request_send(struct fuse_conn *fc, struct fuse_req *req);
 
 /**
- * Send a request for which a reply is not expected
+ * 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_noreply(struct fuse_conn *fc, struct fuse_req *req);
+void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req,
+                        int background);
 
 /**
- * Send asynchronous request
+ * Send a request with no reply
  */
-void request_send_async(struct fuse_conn *fc, struct fuse_req *req,
-                       fuse_reqend_t end);
+void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req);
 
 /**
  * Get the attributes of a file
  */
 int fuse_do_getattr(struct inode *inode);
-
-/**
- * Write dirty pages
- */
-void fuse_sync_inode(struct inode *inode);
index 63973a7d74934ba6b9df0803bc6807c9c9cfa790..a97c3e8c0896e96f0648d3c12cc92ddd21fec489 100644 (file)
@@ -9,10 +9,10 @@
 /* This file defines the kernel interface of FUSE */
 
 /** Version number of this interface */
-#define FUSE_KERNEL_VERSION 4
+#define FUSE_KERNEL_VERSION 5
 
 /** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 2
+#define FUSE_KERNEL_MINOR_VERSION 1
 
 /** The node ID of the root inode */
 #define FUSE_ROOT_ID 1
@@ -77,7 +77,7 @@ enum fuse_opcode {
        FUSE_WRITE         = 16,
        FUSE_STATFS        = 17,
        FUSE_RELEASE       = 18,
-       FUSE_INVALIDATE    = 19, /* user initiated */
+       /*FUSE_INVALIDATE    = 19,*/
        FUSE_FSYNC         = 20,
        FUSE_SETXATTR      = 21,
        FUSE_GETXATTR      = 22,
@@ -212,14 +212,6 @@ struct fuse_out_header {
        int error;
 };
 
-struct fuse_user_header {
-       int unique; /* zero */
-       enum fuse_opcode opcode;
-       unsigned long nodeid;
-       unsigned long ino;  /* Needed only on 2.4.x where ino is also
-                              used for inode lookup */
-};
-
 struct fuse_dirent {
        unsigned long ino;
        unsigned short namelen;
index 5887605564ce64dac48a677c8c13314c3555aef2..ac80b632653be3ac19ab696e4c1f50bd920a50a0 100644 (file)
@@ -1,9 +1,9 @@
 /*
-    FUSE: Filesystem in Userspace
-    Copyright (C) 2001-2004  Miklos Szeredi <miklos@szeredi.hu>
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2004  Miklos Szeredi <miklos@szeredi.hu>
 
-    This program can be distributed under the terms of the GNU GPL.
-    See the file COPYING.
+  This program can be distributed under the terms of the GNU GPL.
+  See the file COPYING.
 */
 
 #include "fuse_i.h"
@@ -72,8 +72,6 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
                kmem_cache_free(fuse_inode_cachep, inode);
                return NULL;
        }
-       init_rwsem(&fi->write_sem);
-       INIT_LIST_HEAD(&fi->write_files);
 
        return inode;
 }
@@ -81,7 +79,6 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
 static void fuse_destroy_inode(struct inode *inode)
 {
        struct fuse_inode *fi = get_fuse_inode(inode);
-       BUG_ON(!list_empty(&fi->write_files));
        if (fi->forget_req)
                fuse_request_free(fi->forget_req);
        kmem_cache_free(fuse_inode_cachep, inode);
@@ -119,10 +116,8 @@ static void fuse_put_super(struct super_block *sb)
 {
        struct fuse_conn *fc = get_fuse_conn_super(sb);
 
-       down(&fc->sb_sem);
        spin_lock(&fuse_lock);
        fc->sb = NULL;
-       up(&fc->sb_sem);
        fc->uid = 0;
        fc->flags = 0;
        /* Flush all readers on this fs */
@@ -177,7 +172,7 @@ enum {
        OPT_ALLOW_OTHER,
        OPT_ALLOW_ROOT,
        OPT_KERNEL_CACHE,
-#ifndef FUSE_MAINLINE
+#ifndef KERNEL_2_6
        OPT_LARGE_READ,
 #endif
        OPT_DIRECT_IO,
@@ -193,7 +188,7 @@ static match_table_t tokens = {
        {OPT_ALLOW_OTHER,               "allow_other"},
        {OPT_ALLOW_ROOT,                "allow_root"},
        {OPT_KERNEL_CACHE,              "kernel_cache"},
-#ifndef FUSE_MAINLINE
+#ifndef KERNEL_2_6
        {OPT_LARGE_READ,                "large_read"},
 #endif
        {OPT_DIRECT_IO,                 "direct_io"},
@@ -251,19 +246,9 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d)
                        d->flags |= FUSE_KERNEL_CACHE;
                        break;
 
-#ifndef FUSE_MAINLINE
-               case OPT_LARGE_READ:
 #ifndef KERNEL_2_6
+               case OPT_LARGE_READ:
                        d->flags |= FUSE_LARGE_READ;
-#else
-                       {
-                               static int warned = 0;
-                               if (!warned) {
-                                       printk("fuse: large_read option is deprecated for 2.6 kernels\n");
-                                       warned = 1;
-                               }
-                       }
-#endif
                        break;
 #endif
                case OPT_DIRECT_IO:
@@ -345,7 +330,6 @@ static struct fuse_conn *new_conn(void)
                INIT_LIST_HEAD(&fc->processing);
                INIT_LIST_HEAD(&fc->unused_list);
                sema_init(&fc->unused_sem, FUSE_MAX_OUTSTANDING);
-               sema_init(&fc->sb_sem, 1);
                for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) {
                        struct fuse_req *req = fuse_request_alloc();
                        if (!req) {
@@ -368,18 +352,13 @@ static struct fuse_conn *get_conn(struct file *file, struct super_block *sb)
 {
        struct fuse_conn *fc;
 
-       if (file->f_op != &fuse_dev_operations) {
-               printk("FUSE: bad communication file descriptor\n");
+       if (file->f_op != &fuse_dev_operations)
                return NULL;
-       }
        fc = new_conn();
-       if (fc == NULL) {
-               printk("FUSE: failed to allocate connection data\n");
+       if (fc == NULL)
                return NULL;
-       }
        spin_lock(&fuse_lock);
        if (file->private_data) {
-               printk("fuse_read_super: connection already mounted\n");
                free_conn(fc);
                fc = NULL;
        } else {
@@ -454,7 +433,6 @@ static int fuse_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len,
        return type;
 }
 
-
 static struct export_operations fuse_export_operations = {
        .get_dentry     = fuse_get_dentry,
        .encode_fh      = fuse_encode_fh,
@@ -516,10 +494,8 @@ static int fuse_read_super(struct super_block *sb, void *data, int silent)
        *get_fuse_conn_super_p(sb) = fc;
 
        root = get_root_inode(sb, d.rootmode);
-       if (root == NULL) {
-               printk("fuse_read_super: failed to get root inode\n");
+       if (root == NULL)
                goto err;
-       }
 
        sb->s_root = d_alloc_root(root);
        if (!sb->s_root) {
@@ -530,10 +506,8 @@ static int fuse_read_super(struct super_block *sb, void *data, int silent)
        return 0;
 
  err:
-       down(&fc->sb_sem);
        spin_lock(&fuse_lock);
        fc->sb = NULL;
-       up(&fc->sb_sem);
        fuse_release_conn(fc);
        spin_unlock(&fuse_lock);
        *get_fuse_conn_super_p(sb) = NULL;
index 8dca167a80bc9acb985f0060761ea0eecf7f843d..57375f4a8c5908124b817a4e7c4feb224556a4c2 100644 (file)
@@ -1,9 +1,9 @@
 /*
-    FUSE: Filesystem in Userspace
-    Copyright (C) 2001-2004  Miklos Szeredi <miklos@szeredi.hu>
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2004  Miklos Szeredi <miklos@szeredi.hu>
 
-    This program can be distributed under the terms of the GNU GPL.
-    See the file COPYING.
+  This program can be distributed under the terms of the GNU GPL.
+  See the file COPYING.
 */
 
 #include "fuse_i.h"
index 99bca56e65703d56ba225e914b398d7e780f94fa..1e120e8752f459dc8f01ce4446f1a46f889b47ab 100644 (file)
@@ -235,43 +235,6 @@ static struct node *find_node(struct fuse *f, nodeid_t parent, char *name,
     return node;
 }
 
-static int path_lookup(struct fuse *f, const char *path, nodeid_t *nodeidp,
-                       unsigned long *inop)
-{
-    nodeid_t nodeid;
-    unsigned long ino;
-    int err;
-    char *s;
-    char *name;
-    char *tmp = strdup(path);
-    if (!tmp)
-        return -ENOMEM;
-
-    pthread_mutex_lock(&f->lock);
-    nodeid = FUSE_ROOT_ID;
-    ino = nodeid;
-    err = 0;
-    for  (s = tmp; (name = strsep(&s, "/")) != NULL; ) {
-        if (name[0]) {
-            struct node *node = lookup_node(f, nodeid, name);
-            if (node == NULL) {
-                err = -ENOENT;
-                break;
-            }
-            nodeid = node->nodeid;
-            ino = node->ino;
-        }
-    }
-    pthread_mutex_unlock(&f->lock);
-    free(tmp);
-    if (!err) {
-        *nodeidp = nodeid;
-        *inop = ino;
-    }
-    
-    return err;
-}
-
 static char *add_name(char *buf, char *s, const char *name)
 {
     size_t len = strlen(name);
@@ -1664,7 +1627,7 @@ struct fuse_cmd *fuse_read_cmd(struct fuse *f)
     res = read(f->fd, cmd->buf, FUSE_MAX_IN);
     if (res == -1) {
         free_cmd(cmd);
-        if (fuse_exited(f) || errno == EINTR)
+        if (fuse_exited(f) || errno == EINTR || errno == ENOENT)
             return NULL;
         
         /* ENODEV means we got unmounted, so we silenty return failure */
@@ -1718,38 +1681,9 @@ int fuse_loop(struct fuse *f)
 
 int fuse_invalidate(struct fuse *f, const char *path)
 {
-    int res;
-    int err;
-    nodeid_t nodeid;
-    unsigned long ino;
-    struct fuse_user_header h;
-
-    err = path_lookup(f, path, &nodeid, &ino);
-    if (err) {
-        if (err == -ENOENT)
-            return 0;
-        else
-            return err;
-    }
-
-    memset(&h, 0, sizeof(struct fuse_user_header));
-    h.opcode = FUSE_INVALIDATE;
-    h.nodeid = nodeid;
-    h.ino = ino;
-    
-    if ((f->flags & FUSE_DEBUG)) {
-        printf("INVALIDATE nodeid: %li\n", nodeid);
-        fflush(stdout);
-    }
-
-    res = write(f->fd, &h, sizeof(struct fuse_user_header));
-    if (res == -1) {
-        if (errno != ENOENT) {
-            perror("fuse: writing device");
-            return -errno;
-        }
-    }
-    return 0;
+    (void) f;
+    (void) path;
+    return -EINVAL;
 }
 
 void fuse_exit(struct fuse *f)
@@ -1829,8 +1763,10 @@ static int parse_lib_opts(struct fuse *f, const char *opts)
         char *s = xopts;
         char *opt;
 
-        if (xopts == NULL)
+        if (xopts == NULL) {
+            fprintf(stderr, "fuse: memory allocation failed\n");
             return -1;
+        }
         
         while((opt = strsep(&s, ","))) {
             if (strcmp(opt, "debug") == 0)
@@ -1860,8 +1796,10 @@ struct fuse *fuse_new_common(int fd, const char *opts,
     }
 
     f = (struct fuse *) calloc(1, sizeof(struct fuse));
-    if (f == NULL)
+    if (f == NULL) {
+        fprintf(stderr, "fuse: failed to allocate fuse object\n");
         goto out;
+    }
 
     if (check_version(f) == -1)
         goto out_free;
@@ -1876,14 +1814,18 @@ struct fuse *fuse_new_common(int fd, const char *opts,
     f->name_table_size = 14057;
     f->name_table = (struct node **)
         calloc(1, sizeof(struct node *) * f->name_table_size);
-    if (f->name_table == NULL)
+    if (f->name_table == NULL) {
+        fprintf(stderr, "fuse: memory allocation failed\n");
         goto out_free;
+    }
 
     f->id_table_size = 14057;
     f->id_table = (struct node **)
         calloc(1, sizeof(struct node *) * f->id_table_size);
-    if (f->id_table == NULL)
+    if (f->id_table == NULL) {
+        fprintf(stderr, "fuse: memory allocation failed\n");
         goto out_free_name_table;
+    }
 
 #ifndef USE_UCLIBC
      pthread_mutex_init(&f->lock, NULL);
@@ -1903,14 +1845,18 @@ struct fuse *fuse_new_common(int fd, const char *opts,
     f->exited = 0;
 
     root = (struct node *) calloc(1, sizeof(struct node));
-    if (root == NULL)
+    if (root == NULL) {
+        fprintf(stderr, "fuse: memory allocation failed\n");
         goto out_free_id_table;
+    }
 
     root->mode = 0;
     root->rdev = 0;
     root->name = strdup("/");
-    if (root->name == NULL)
+    if (root->name == NULL) {
+        fprintf(stderr, "fuse: memory allocation failed\n");
         goto out_free_root;
+    }
 
     root->parent = 0;
     root->nodeid = FUSE_ROOT_ID;
@@ -1928,7 +1874,6 @@ struct fuse *fuse_new_common(int fd, const char *opts,
  out_free:
     free(f);
  out:
-    fprintf(stderr, "fuse: failed to allocate fuse object\n");
     return NULL;
 }
 
index 329c4adb755c4d4fdbe59898741a6ce2073d2005..edbed43d54d15fc3fb0ed3dc02dd516f64f736e1 100644 (file)
@@ -9,6 +9,7 @@ FUSE_2.2 {
                fuse_loop;
                fuse_loop_mt;
                fuse_loop_mt_proc;
+               fuse_main;
                fuse_main_compat1;
                fuse_main_compat2;
                fuse_main_real;
index 1ac5aa2a58c4bb598cbd4001ce553524dd65ff48..c14248d68b76fabe6991587d3ba785feb18ace8a 100644 (file)
@@ -379,6 +379,13 @@ int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
     return fuse_main_common(argc, argv, op, op_size, 0);
 }
 
+#undef fuse_main
+int fuse_main()
+{
+    fprintf(stderr, "This function does not exist\n");
+    return -1;
+}
+
 void fuse_main_compat1(int argc, char *argv[],
                       const struct fuse_operations_compat1 *op)
 {
index 40df4902088de5b4cd809a530ca184f8b75026e1..80808c8fc4deed5d3f56e427dfa24c09067780a5 100644 (file)
@@ -33,6 +33,7 @@
 #include <sys/fsuid.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <sys/utsname.h>
 
 #define FUSE_COMMFD_ENV         "_FUSE_COMMFD"
 
@@ -463,15 +464,31 @@ static int do_mount(const char *mnt, const char *type, mode_t rootmode,
                    !begins_with(s, "uid=")) {
             int on;
             int flag;
-            if (find_mount_flag(s, len, &on, &flag)) {
-                if (on)
-                    flags |= flag;
-                else
-                    flags  &= ~flag;
-            } else {
-                memcpy(d, s, len);
-                d += len;
-                *d++ = ',';
+            int skip_option = 0;
+            const char *large_read_opt = "large_read";
+            if (len == strlen(large_read_opt) &&
+                strncmp(large_read_opt, s, len) == 0) {
+                struct utsname utsname;
+                unsigned kmaj, kmin;
+                res = uname(&utsname);
+                if (res == 0 && 
+                    sscanf(utsname.release, "%u.%u", &kmaj, &kmin) == 2 &&
+                    (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
+                    fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
+                    skip_option = 1;
+                }
+            }
+            if (!skip_option) {
+                if (find_mount_flag(s, len, &on, &flag)) {
+                    if (on)
+                        flags |= flag;
+                    else
+                        flags  &= ~flag;
+                } else {
+                    memcpy(d, s, len);
+                    d += len;
+                    *d++ = ',';
+                }
             }
         }
         s += len;