+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
Name: OW
-Author: Paul H. Alfille <palfille at partners org>
+Author: Paul H. Alfille palfille at partners org
Homepage: http://owfs.sourceforge.net
==============================================================================
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
==============================================================================
Name: EncFS
-Author: Valient Gough <vgough at pobox com>
+Author: Valient Gough vgough at pobox com
Homepage: http://pobox.com/~vgough/encfs.html
==============================================================================
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/
==============================================================================
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/
==============================================================================
Name: Run-Time-Access
-Author: Bob Smith <bsmith at linuxtoys org>
+Author: Bob Smith bsmith at linuxtoys org
Homepage: http://www.runtimeaccess.com
==============================================================================
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
==============================================================================
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
==============================================================================
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
==============================================================================
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
==============================================================================
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
==============================================================================
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
==============================================================================
Name: Fusedav
-Author: Lennart Poettering <mzshfrqni (at) 0pointer (dot) de>
+Author: Lennart Poettering mzshfrqni at 0pointer de
Homepage: http://0pointer.de/lennart/projects/fusedav/
==============================================================================
Name: RelFS
-Author: Vincenzo Ciancia <vincenzo_ml (at) yahoo (dot) it>
+Author: Vincenzo Ciancia vincenzo_ml at yahoo it
Homepage: http://relfs.sourceforge.net/
==============================================================================
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
==============================================================================
Name: DataDraw
-Author: Bill Cox <bill (at) viasic (dot) com>
+Author: Bill Cox bill at viasic com
Homepage: http://www.viasic.com/opensource/
==============================================================================
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/
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.
+
+==============================================================================
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
* 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.
/** 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
* 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 *);
* ----------------------------------------------------------- */
#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
--- /dev/null
+/*
+ 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"
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
/*
- 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/init.h>
#include <linux/module.h>
#include <linux/poll.h>
+#include <linux/uio.h>
#ifdef KERNEL_2_6
#include <linux/kobject.h>
#else
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)
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(¤t->sigmask_lock);
+ *oldset = current->blocked;
+ siginitsetinv(¤t->blocked, sigmask(SIGKILL) & ~oldset->sig[0]);
+ recalc_sigpending(current);
+ spin_unlock_irq(¤t->sigmask_lock);
+}
+
+static inline void restore_sigs(sigset_t *oldset)
+{
+ spin_lock_irq(¤t->sigmask_lock);
+ current->blocked = *oldset;
+ recalc_sigpending(current);
+ spin_unlock_irq(¤t->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;
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;
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);
}
}
-/* 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);
}
}
-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(¤t->mm->mmap_sem);
+ err = get_user_pages(current, current->mm, cs->addr, 1, cs->write, 0,
+ &cs->pg, NULL);
+ up_read(¤t->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)
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);
}
}
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,
};
if (!fuse_req_cachep)
goto out_version_clean;
-
err = misc_register(&fuse_miscdevice);
if (err)
goto out_cache_clean;
/*
- 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"
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);
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;
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;
}
#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);
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,
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);
/* 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,
int err;
if (!req)
- return -ERESTARTSYS;
+ return -ERESTARTNOINTR;
memset(&inarg, 0, sizeof(inarg));
inarg.mode = mode;
int err;
if (!req)
- return -ERESTARTSYS;
+ return -ERESTARTNOINTR;
memset(&inarg, 0, sizeof(inarg));
inarg.mode = mode;
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);
int err;
if (!req)
- return -ERESTARTSYS;
+ return -ERESTARTNOINTR;
req->in.h.opcode = FUSE_UNLINK;
req->in.h.nodeid = get_node_id(dir);
int err;
if (!req)
- return -ERESTARTSYS;
+ return -ERESTARTNOINTR;
req->in.h.opcode = FUSE_RMDIR;
req->in.h.nodeid = get_node_id(dir);
int err;
if (!req)
- return -ERESTARTSYS;
+ return -ERESTARTNOINTR;
memset(&inarg, 0, sizeof(inarg));
inarg.newdir = get_node_id(newdir);
int err;
if (!req)
- return -ERESTARTSYS;
+ return -ERESTARTNOINTR;
memset(&inarg, 0, sizeof(inarg));
inarg.newdir = get_node_id(newdir);
int err;
if (!req)
- return -ERESTARTSYS;
+ return -ERESTARTNOINTR;
req->in.h.opcode = FUSE_GETATTR;
req->in.h.nodeid = get_node_id(inode);
#ifdef KERNEL_2_6_10_PLUS
err = generic_permission(inode, mask, NULL);
#else
- err = vfs_permission(inode, mask);
+ err = vfs_permission(inode, mask);
#endif
}
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;
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;
int err;
if (!req)
- return -ERESTARTSYS;
+ return -ERESTARTNOINTR;
req->in.h.opcode = FUSE_GETDIR;
req->in.h.nodeid = get_node_id(inode);
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);
char *link;
if (!req)
- return ERR_PTR(-ERESTARTSYS);
+ return ERR_PTR(-ERESTARTNOINTR);
link = (char *) __get_free_page(GFP_KERNEL);
if (!link) {
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);
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;
}
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))
(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);
req = fuse_get_request(fc);
if (!req)
- return -ERESTARTSYS;
+ return -ERESTARTNOINTR;
memset(&inarg, 0, sizeof(inarg));
inarg.size = size;
req = fuse_get_request(fc);
if (!req)
- return -ERESTARTSYS;
+ return -ERESTARTNOINTR;
memset(&inarg, 0, sizeof(inarg));
inarg.size = size;
req = fuse_get_request(fc);
if (!req)
- return -ERESTARTSYS;
+ return -ERESTARTNOINTR;
memset(&inarg, 0, sizeof(inarg));
inarg.size = size;
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);
/*
- 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);
return err;
}
+ /* Prevent concurrent unlink or rename */
down(&inode->i_sem);
err = -ERESTARTSYS;
req = fuse_get_request(fc);
else {
ff->fh = outarg.fh;
file->private_data = ff;
- INIT_LIST_HEAD(&ff->ff_list);
}
out_put_request:
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;
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;
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;
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;
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;
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)
}
#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 {
(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 ++;
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
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++) {
}
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++) {
page_cache_release(page);
}
}
+ return err;
}
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);
}
}
}
#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)
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);
}
}
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);
}
}
{
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;
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;
else
return generic_file_read(file, buf, count, ppos);
#endif
-
}
static ssize_t fuse_file_write(struct file *file, const char __user *buf,
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);
}
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
/*
- 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
# 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 */
/** 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 */
};
/** One output argument of a request */
-struct fuse_out_arg {
+struct fuse_arg {
unsigned size;
void *value;
};
/** 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;
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;
/** 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 */
/** 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;
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);
/* 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
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,
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;
/*
- 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"
kmem_cache_free(fuse_inode_cachep, inode);
return NULL;
}
- init_rwsem(&fi->write_sem);
- INIT_LIST_HEAD(&fi->write_files);
return inode;
}
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);
{
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 */
OPT_ALLOW_OTHER,
OPT_ALLOW_ROOT,
OPT_KERNEL_CACHE,
-#ifndef FUSE_MAINLINE
+#ifndef KERNEL_2_6
OPT_LARGE_READ,
#endif
OPT_DIRECT_IO,
{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"},
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:
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) {
{
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 {
return type;
}
-
static struct export_operations fuse_export_operations = {
.get_dentry = fuse_get_dentry,
.encode_fh = fuse_encode_fh,
*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) {
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;
/*
- 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"
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);
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 */
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)
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)
}
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;
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);
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;
out_free:
free(f);
out:
- fprintf(stderr, "fuse: failed to allocate fuse object\n");
return NULL;
}
fuse_loop;
fuse_loop_mt;
fuse_loop_mt_proc;
+ fuse_main;
fuse_main_compat1;
fuse_main_compat2;
fuse_main_real;
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)
{
#include <sys/fsuid.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <sys/utsname.h>
#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
!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;