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