aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/dev.c149
-rw-r--r--kernel/dir.c322
-rw-r--r--kernel/file.c45
-rw-r--r--kernel/fuse_i.h72
-rw-r--r--kernel/fuse_kernel.h151
-rw-r--r--kernel/inode.c173
6 files changed, 524 insertions, 388 deletions
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 @@ void fuse_request_free(struct fuse_req *req)
kmem_cache_free(fuse_req_cachep, req);
}
-static int get_unique(struct fuse_conn *fc)
-{
- fc->reqctr++;
- /* zero is special */
- if (fc->reqctr == 0)
- fc->reqctr = 1;
- return fc->reqctr;
-}
-
#ifdef KERNEL_2_6
static inline void block_sigs(sigset_t *oldset)
{
@@ -112,6 +103,7 @@ static void __fuse_get_request(struct fuse_req *req)
/* Must be called with > 1 refcount */
static void __fuse_put_request(struct fuse_req *req)
{
+ BUG_ON(atomic_read(&req->count) < 2);
atomic_dec(&req->count);
}
@@ -134,7 +126,7 @@ static struct fuse_req *do_get_request(struct fuse_conn *fc)
struct fuse_req *fuse_get_request(struct fuse_conn *fc)
{
- if (down_interruptible(&fc->unused_sem))
+ if (down_interruptible(&fc->outstanding_sem))
return NULL;
return do_get_request(fc);
}
@@ -145,7 +137,7 @@ struct fuse_req *fuse_get_request_nonint(struct fuse_conn *fc)
sigset_t oldset;
block_sigs(&oldset);
- intr = down_interruptible(&fc->unused_sem);
+ intr = down_interruptible(&fc->outstanding_sem);
restore_sigs(&oldset);
return intr ? NULL : do_get_request(fc);
}
@@ -154,12 +146,16 @@ void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req)
{
if (!req->preallocated)
fuse_request_free(req);
- else {
- spin_lock(&fuse_lock);
+
+ spin_lock(&fuse_lock);
+ if (req->preallocated)
list_add(&req->list, &fc->unused_list);
- spin_unlock(&fuse_lock);
- up(&fc->unused_sem);
- }
+
+ if (fc->outstanding_debt)
+ fc->outstanding_debt--;
+ else
+ up(&fc->outstanding_sem);
+ spin_unlock(&fuse_lock);
}
void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
@@ -175,6 +171,14 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
req->finished = 1;
putback = atomic_dec_and_test(&req->count);
spin_unlock(&fuse_lock);
+ if (req->background) {
+ if (req->inode)
+ iput(req->inode);
+ if (req->inode2)
+ iput(req->inode2);
+ if (req->file)
+ fput(req->file);
+ }
wake_up(&req->waitq);
if (putback)
fuse_putback_request(fc, req);
@@ -190,9 +194,21 @@ static int request_wait_answer_nonint(struct fuse_req *req)
return err;
}
-/* Called with fuse_lock held. Releases, and then reaquires it. */
-static void request_wait_answer(struct fuse_req *req, int interruptible,
- int background)
+static void background_request(struct fuse_req *req)
+{
+ /* Need to get hold of the inode(s) and/or file used in the
+ request, so FORGET and RELEASE are not sent too early */
+ req->background = 1;
+ if (req->inode)
+ req->inode = igrab(req->inode);
+ if (req->inode2)
+ req->inode2 = igrab(req->inode2);
+ if (req->file)
+ get_file(req->file);
+}
+
+/* Called with fuse_lock held. Releases, and then reacquires it. */
+static void request_wait_answer(struct fuse_req *req, int interruptible)
{
int intr;
@@ -212,10 +228,6 @@ static void request_wait_answer(struct fuse_req *req, int interruptible,
if (!intr)
return;
- if (background && !req->sent) {
- req->isreply = 0;
- return;
- }
if (!interruptible || req->sent)
req->out.h.error = -EINTR;
else
@@ -232,50 +244,74 @@ static void request_wait_answer(struct fuse_req *req, int interruptible,
wait_event(req->waitq, !req->locked);
spin_lock(&fuse_lock);
}
- if (!list_empty(&req->list)) {
- /* request is still on one of the lists */
+ if (!req->sent && !list_empty(&req->list)) {
list_del(&req->list);
__fuse_put_request(req);
+ } else if (req->sent)
+ background_request(req);
+}
+
+static unsigned len_args(unsigned numargs, struct fuse_arg *args)
+{
+ unsigned nbytes = 0;
+ unsigned i;
+
+ for (i = 0; i < numargs; i++)
+ nbytes += args[i].size;
+
+ return nbytes;
+}
+
+static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
+{
+ fc->reqctr++;
+ /* zero is special */
+ if (fc->reqctr == 0)
+ fc->reqctr = 1;
+ req->in.h.unique = fc->reqctr;
+ req->in.h.len = sizeof(struct fuse_in_header) +
+ len_args(req->in.numargs, (struct fuse_arg *) req->in.args);
+ if (!req->preallocated) {
+ /* decrease outstanding_sem, but without blocking... */
+ if (down_trylock(&fc->outstanding_sem))
+ fc->outstanding_debt++;
}
+ list_add_tail(&req->list, &fc->pending);
+ wake_up(&fc->waitq);
}
static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req,
- int interruptible, int background)
+ int interruptible)
{
req->isreply = 1;
spin_lock(&fuse_lock);
req->out.h.error = -ENOTCONN;
if (fc->file) {
- req->in.h.unique = get_unique(fc);
- list_add_tail(&req->list, &fc->pending);
- wake_up(&fc->waitq);
+ queue_request(fc, req);
/* acquire extra reference, since request is still needed
after request_end() */
__fuse_get_request(req);
- request_wait_answer(req, interruptible, background);
+ request_wait_answer(req, interruptible);
}
spin_unlock(&fuse_lock);
}
void request_send(struct fuse_conn *fc, struct fuse_req *req)
{
- request_send_wait(fc, req, 1, 0);
+ request_send_wait(fc, req, 1);
}
-void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req,
- int background)
+void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req)
{
- request_send_wait(fc, req, 0, background);
+ request_send_wait(fc, req, 0);
}
-void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
+void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
{
- req->isreply = 0;
spin_lock(&fuse_lock);
if (fc->file) {
- list_add_tail(&req->list, &fc->pending);
- wake_up(&fc->waitq);
+ queue_request(fc, req);
spin_unlock(&fuse_lock);
} else {
req->out.h.error = -ENOTCONN;
@@ -283,6 +319,19 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
}
}
+void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
+{
+ req->isreply = 0;
+ request_send_nowait(fc, req);
+}
+
+void request_send_background(struct fuse_conn *fc, struct fuse_req *req)
+{
+ req->isreply = 1;
+ background_request(req);
+ request_send_nowait(fc, req);
+}
+
static inline int lock_request(struct fuse_req *req)
{
int err = 0;
@@ -486,17 +535,6 @@ static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs,
return err;
}
-static unsigned len_args(unsigned numargs, struct fuse_arg *args)
-{
- unsigned nbytes = 0;
- unsigned i;
-
- for (i = 0; i < numargs; i++)
- nbytes += args[i].size;
-
- return nbytes;
-}
-
static void request_wait(struct fuse_conn *fc)
{
DECLARE_WAITQUEUE(wait, current);
@@ -544,8 +582,7 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
spin_unlock(&fuse_lock);
in = &req->in;
- reqsize = sizeof(struct fuse_in_header);
- reqsize += len_args(in->numargs, (struct fuse_arg *) in->args);
+ reqsize = req->in.h.len;
nbytes = fuse_copy_init(&cs, 1, req, iov, nr_segs);
err = -EINVAL;
if (nbytes >= reqsize) {
@@ -652,16 +689,22 @@ static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov,
if (err)
goto err_finish;
err = -EINVAL;
- if (!oh.unique || oh.error <= -1000 || oh.error > 0)
+ if (!oh.unique || oh.error <= -1000 || oh.error > 0 ||
+ oh.len != nbytes)
goto err_finish;
spin_lock(&fuse_lock);
req = request_find(fc, oh.unique);
- err = -ENOENT;
+ err = -EINVAL;
if (!req)
goto err_unlock;
list_del_init(&req->list);
+ if (req->interrupted) {
+ request_end(fc, req);
+ fuse_copy_finish(&cs);
+ return -ENOENT;
+ }
req->out.h = oh;
req->locked = 1;
cs.req = req;
diff --git a/kernel/dir.c b/kernel/dir.c
index 19aebb1..08427a9 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -17,159 +17,15 @@
#endif
#include <linux/sched.h>
-static struct inode_operations fuse_dir_inode_operations;
-static struct inode_operations fuse_file_inode_operations;
-static struct inode_operations fuse_symlink_inode_operations;
-static struct file_operations fuse_dir_operations;
-static struct dentry_operations fuse_dentry_operations;
-
-#ifndef KERNEL_2_6
-#define new_decode_dev(x) (x)
-#define new_encode_dev(x) (x)
-#endif
-static void change_attributes(struct inode *inode, struct fuse_attr *attr)
-{
- if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size) {
-#ifdef KERNEL_2_6
- invalidate_inode_pages(inode->i_mapping);
-#else
- invalidate_inode_pages(inode);
-#endif
- }
-
- inode->i_ino = attr->ino;
- inode->i_mode = (inode->i_mode & S_IFMT) + (attr->mode & 07777);
- inode->i_nlink = attr->nlink;
- inode->i_uid = attr->uid;
- inode->i_gid = attr->gid;
- i_size_write(inode, attr->size);
- inode->i_blksize = PAGE_CACHE_SIZE;
- inode->i_blocks = attr->blocks;
-#ifdef KERNEL_2_6
- inode->i_atime.tv_sec = attr->atime;
- inode->i_atime.tv_nsec = attr->atimensec;
- inode->i_mtime.tv_sec = attr->mtime;
- inode->i_mtime.tv_nsec = attr->mtimensec;
- inode->i_ctime.tv_sec = attr->ctime;
- inode->i_ctime.tv_nsec = attr->ctimensec;
-#else
- inode->i_atime = attr->atime;
- inode->i_mtime = attr->mtime;
- inode->i_ctime = attr->ctime;
-#endif
-}
-
-static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
-{
- inode->i_mode = attr->mode & S_IFMT;
- i_size_write(inode, attr->size);
- if (S_ISREG(inode->i_mode)) {
- inode->i_op = &fuse_file_inode_operations;
- fuse_init_file_inode(inode);
- }
- else if (S_ISDIR(inode->i_mode)) {
- inode->i_op = &fuse_dir_inode_operations;
- inode->i_fop = &fuse_dir_operations;
- }
- else if (S_ISLNK(inode->i_mode)) {
- inode->i_op = &fuse_symlink_inode_operations;
- }
- else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
- S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
- inode->i_op = &fuse_file_inode_operations;
- init_special_inode(inode, inode->i_mode,
- new_decode_dev(attr->rdev));
- } else {
- /* Don't let user create weird files */
- inode->i_mode = S_IFREG;
- inode->i_op = &fuse_file_inode_operations;
- fuse_init_file_inode(inode);
- }
-}
-
-#ifdef KERNEL_2_6
-static int fuse_inode_eq(struct inode *inode, void *_nodeidp)
-{
- unsigned long nodeid = *(unsigned long *) _nodeidp;
- if (get_node_id(inode) == nodeid)
- return 1;
- else
- return 0;
-}
-
-static int fuse_inode_set(struct inode *inode, void *_nodeidp)
-{
- unsigned long nodeid = *(unsigned long *) _nodeidp;
- get_fuse_inode(inode)->nodeid = nodeid;
- return 0;
-}
-
-struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
- int generation, struct fuse_attr *attr, int version)
-{
- struct inode *inode;
- struct fuse_conn *fc = get_fuse_conn_super(sb);
-
- inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid);
- if (!inode)
- return NULL;
-
- if ((inode->i_state & I_NEW)) {
- inode->i_generation = generation;
- inode->i_data.backing_dev_info = &fc->bdi;
- fuse_init_inode(inode, attr);
- unlock_new_inode(inode);
- }
-
- change_attributes(inode, attr);
- inode->i_version = version;
- return inode;
-}
-
-struct inode *fuse_ilookup(struct super_block *sb, unsigned long nodeid)
+static inline unsigned long time_to_jiffies(unsigned long sec,
+ unsigned long nsec)
{
- return ilookup5(sb, nodeid, fuse_inode_eq, &nodeid);
-}
-#else
-static int fuse_inode_eq(struct inode *inode, unsigned long ino, void *_nodeidp){
- unsigned long nodeid = *(unsigned long *) _nodeidp;
- if (inode->u.generic_ip && get_node_id(inode) == nodeid)
- return 1;
- else
+ /* prevent wrapping of jiffies */
+ if (sec + 1 >= LONG_MAX / HZ)
return 0;
-}
-
-struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
- int generation, struct fuse_attr *attr, int version)
-{
- struct inode *inode;
-
- inode = iget4(sb, attr->ino, fuse_inode_eq, &nodeid);
- if (!inode)
- return NULL;
- if (!inode->u.generic_ip) {
- get_fuse_inode(inode)->nodeid = nodeid;
- inode->u.generic_ip = inode;
- inode->i_generation = generation;
- fuse_init_inode(inode, attr);
- }
-
- change_attributes(inode, attr);
- inode->i_version = version;
- return inode;
-}
-
-struct inode *fuse_ilookup(struct super_block *sb, ino_t ino, unsigned long nodeid)
-{
- struct inode *inode = iget4(sb, ino, fuse_inode_eq, &nodeid);
- if (inode && !inode->u.generic_ip) {
- iput(inode);
- inode = NULL;
- }
- return inode;
+ return jiffies + sec * HZ + nsec / (1000000000 / HZ);
}
-#endif
static void fuse_lookup_init(struct fuse_req *req, struct inode *dir,
struct dentry *entry,
@@ -177,6 +33,7 @@ static void fuse_lookup_init(struct fuse_req *req, struct inode *dir,
{
req->in.h.opcode = FUSE_LOOKUP;
req->in.h.nodeid = get_node_id(dir);
+ req->inode = dir;
req->in.numargs = 1;
req->in.args[0].size = entry->d_name.len + 1;
req->in.args[0].value = entry->d_name.name;
@@ -185,15 +42,53 @@ static void fuse_lookup_init(struct fuse_req *req, struct inode *dir,
req->out.args[0].value = outarg;
}
-static inline unsigned long time_to_jiffies(unsigned long sec,
- unsigned long nsec)
+static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
{
- /* prevent wrapping of jiffies */
- if (sec + 1 >= LONG_MAX / HZ)
+ if (!entry->d_inode || is_bad_inode(entry->d_inode))
return 0;
+ else if (entry->d_time && time_after(jiffies, entry->d_time)) {
+ int err;
+ int version;
+ struct fuse_entry_out outarg;
+ struct inode *inode = entry->d_inode;
+ struct fuse_inode *fi = get_fuse_inode(inode);
+ struct fuse_conn *fc = get_fuse_conn(inode);
+ struct fuse_req *req = fuse_get_request_nonint(fc);
+ if (!req)
+ return 0;
- return jiffies + sec * HZ + nsec / (1000000000 / HZ);
+ fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg);
+ request_send_nonint(fc, req);
+ version = req->out.h.unique;
+ err = req->out.h.error;
+ fuse_put_request(fc, req);
+ if (err || outarg.nodeid != get_node_id(inode) ||
+ (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
+ return 0;
+
+ fuse_change_attributes(inode, &outarg.attr);
+ inode->i_version = version;
+ entry->d_time = time_to_jiffies(outarg.entry_valid,
+ outarg.entry_valid_nsec);
+ fi->i_time = time_to_jiffies(outarg.attr_valid,
+ outarg.attr_valid_nsec);
+ }
+ return 1;
+}
+#ifndef KERNEL_2_6
+static int fuse_dentry_revalidate_2_4(struct dentry *entry, int flags)
+{
+ return fuse_dentry_revalidate(entry, NULL);
}
+#endif
+
+static struct dentry_operations fuse_dentry_operations = {
+#ifdef KERNEL_2_6
+ .d_revalidate = fuse_dentry_revalidate,
+#else
+ .d_revalidate = fuse_dentry_revalidate_2_4,
+#endif
+};
static int fuse_lookup_iget(struct inode *dir, struct dentry *entry,
struct inode **inodep)
@@ -207,6 +102,7 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry,
if (entry->d_name.len > FUSE_NAME_MAX)
return -ENAMETOOLONG;
+
req = fuse_get_request(fc);
if (!req)
return -ERESTARTNOINTR;
@@ -262,6 +158,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
int err;
req->in.h.nodeid = get_node_id(dir);
+ req->inode = dir;
req->out.numargs = 1;
req->out.args[0].size = sizeof(outarg);
req->out.args[0].value = &outarg;
@@ -377,6 +274,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
req->in.h.opcode = FUSE_UNLINK;
req->in.h.nodeid = get_node_id(dir);
+ req->inode = dir;
req->in.numargs = 1;
req->in.args[0].size = entry->d_name.len + 1;
req->in.args[0].value = entry->d_name.name;
@@ -407,6 +305,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
req->in.h.opcode = FUSE_RMDIR;
req->in.h.nodeid = get_node_id(dir);
+ req->inode = dir;
req->in.numargs = 1;
req->in.args[0].size = entry->d_name.len + 1;
req->in.args[0].value = entry->d_name.name;
@@ -435,6 +334,8 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
inarg.newdir = get_node_id(newdir);
req->in.h.opcode = FUSE_RENAME;
req->in.h.nodeid = get_node_id(olddir);
+ req->inode = olddir;
+ req->inode2 = newdir;
req->in.numargs = 3;
req->in.args[0].size = sizeof(inarg);
req->in.args[0].value = &inarg;
@@ -454,8 +355,7 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
rename actually took place. If the invalidation
fails (e.g. some process has CWD under the renamed
directory), then there can be inconsistency between
- the dcache and the real filesystem. But not a lot
- can be done about that */
+ the dcache and the real filesystem. Tough luck. */
fuse_invalidate_entry(oldent);
if (newent->d_inode)
fuse_invalidate_entry(newent);
@@ -478,6 +378,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
memset(&inarg, 0, sizeof(inarg));
inarg.newdir = get_node_id(newdir);
req->in.h.opcode = FUSE_LINK;
+ req->inode2 = newdir;
req->in.numargs = 2;
req->in.args[0].size = sizeof(inarg);
req->in.args[0].value = &inarg;
@@ -506,6 +407,7 @@ int fuse_do_getattr(struct inode *inode)
req->in.h.opcode = FUSE_GETATTR;
req->in.h.nodeid = get_node_id(inode);
+ req->inode = inode;
req->out.numargs = 1;
req->out.args[0].size = sizeof(arg);
req->out.args[0].value = &arg;
@@ -513,10 +415,15 @@ int fuse_do_getattr(struct inode *inode)
err = req->out.h.error;
fuse_put_request(fc, req);
if (!err) {
- struct fuse_inode *fi = get_fuse_inode(inode);
- change_attributes(inode, &arg.attr);
- fi->i_time = time_to_jiffies(arg.attr_valid,
- arg.attr_valid_nsec);
+ if ((inode->i_mode ^ arg.attr.mode) & S_IFMT) {
+ make_bad_inode(inode);
+ err = -EIO;
+ } else {
+ struct fuse_inode *fi = get_fuse_inode(inode);
+ fuse_change_attributes(inode, &arg.attr);
+ fi->i_time = time_to_jiffies(arg.attr_valid,
+ arg.attr_valid_nsec);
+ }
}
return err;
}
@@ -594,7 +501,7 @@ static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
size_t reclen = FUSE_DIRENT_SIZE(dirent);
int over;
- if (dirent->namelen > NAME_MAX)
+ if (dirent->namelen > FUSE_NAME_MAX)
return -EIO;
if (reclen > nbytes)
break;
@@ -640,6 +547,7 @@ static int fuse_getdir(struct file *file)
req->in.h.opcode = FUSE_GETDIR;
req->in.h.nodeid = get_node_id(inode);
+ req->inode = inode;
req->out.numargs = 1;
req->out.args[0].size = sizeof(struct fuse_getdir_out);
req->out.args[0].value = &outarg;
@@ -694,6 +602,7 @@ static char *read_link(struct dentry *dentry)
}
req->in.h.opcode = FUSE_READLINK;
req->in.h.nodeid = get_node_id(inode);
+ req->inode = inode;
req->out.argvar = 1;
req->out.numargs = 1;
req->out.args[0].size = PAGE_SIZE - 1;
@@ -823,6 +732,7 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
inarg.valid = iattr_to_fattr(attr, &inarg.attr);
req->in.h.opcode = FUSE_SETATTR;
req->in.h.nodeid = get_node_id(inode);
+ req->inode = inode;
req->in.numargs = 1;
req->in.args[0].size = sizeof(inarg);
req->in.args[0].value = &inarg;
@@ -833,57 +743,26 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
err = req->out.h.error;
fuse_put_request(fc, req);
if (!err) {
- if (is_truncate) {
- loff_t origsize = i_size_read(inode);
- i_size_write(inode, outarg.attr.size);
- if (origsize > outarg.attr.size)
- vmtruncate(inode, outarg.attr.size);
+ if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
+ make_bad_inode(inode);
+ err = -EIO;
+ } else {
+ if (is_truncate) {
+ loff_t origsize = i_size_read(inode);
+ i_size_write(inode, outarg.attr.size);
+ if (origsize > outarg.attr.size)
+ vmtruncate(inode, outarg.attr.size);
+ }
+ fuse_change_attributes(inode, &outarg.attr);
+ fi->i_time = time_to_jiffies(outarg.attr_valid,
+ outarg.attr_valid_nsec);
}
- change_attributes(inode, &outarg.attr);
- fi->i_time = time_to_jiffies(outarg.attr_valid,
- outarg.attr_valid_nsec);
} else if (err == -EINTR)
fuse_invalidate_attr(inode);
return err;
}
-static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
-{
- if (!entry->d_inode)
- return 0;
- else if (entry->d_time && time_after(jiffies, entry->d_time)) {
- int err;
- int version;
- struct fuse_entry_out outarg;
- struct inode *inode = entry->d_inode;
- struct fuse_inode *fi = get_fuse_inode(inode);
- struct fuse_conn *fc = get_fuse_conn(inode);
- struct fuse_req *req = fuse_get_request_nonint(fc);
- if (!req)
- return 0;
-
- fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg);
- request_send_nonint(fc, req, 0);
- version = req->out.h.unique;
- err = req->out.h.error;
- fuse_put_request(fc, req);
- if (err)
- return 0;
-
- if (outarg.nodeid != get_node_id(inode))
- return 0;
-
- change_attributes(inode, &outarg.attr);
- inode->i_version = version;
- entry->d_time = time_to_jiffies(outarg.entry_valid,
- outarg.entry_valid_nsec);
- fi->i_time = time_to_jiffies(outarg.attr_valid,
- outarg.attr_valid_nsec);
- }
- return 1;
-}
-
#ifdef KERNEL_2_6
static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
struct kstat *stat)
@@ -932,11 +811,6 @@ static int fuse_mknod_2_4(struct inode *dir, struct dentry *entry, int mode,
return fuse_mknod(dir, entry, mode, rdev);
}
-static int fuse_dentry_revalidate_2_4(struct dentry *entry, int flags)
-{
- return fuse_dentry_revalidate(entry, NULL);
-}
-
static int fuse_create_2_4(struct inode *dir, struct dentry *entry, int mode)
{
return fuse_create(dir, entry, mode, NULL);
@@ -978,6 +852,7 @@ static int fuse_setxattr(struct dentry *entry, const char *name,
inarg.flags = flags;
req->in.h.opcode = FUSE_SETXATTR;
req->in.h.nodeid = get_node_id(inode);
+ req->inode = inode;
req->in.numargs = 3;
req->in.args[0].size = sizeof(inarg);
req->in.args[0].value = &inarg;
@@ -1016,6 +891,7 @@ static ssize_t fuse_getxattr(struct dentry *entry, const char *name,
inarg.size = size;
req->in.h.opcode = FUSE_GETXATTR;
req->in.h.nodeid = get_node_id(inode);
+ req->inode = inode;
req->in.numargs = 2;
req->in.args[0].size = sizeof(inarg);
req->in.args[0].value = &inarg;
@@ -1065,6 +941,7 @@ static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
inarg.size = size;
req->in.h.opcode = FUSE_LISTXATTR;
req->in.h.nodeid = get_node_id(inode);
+ req->inode = inode;
req->in.numargs = 1;
req->in.args[0].size = sizeof(inarg);
req->in.args[0].value = &inarg;
@@ -1108,6 +985,7 @@ static int fuse_removexattr(struct dentry *entry, const char *name)
req->in.h.opcode = FUSE_REMOVEXATTR;
req->in.h.nodeid = get_node_id(inode);
+ req->inode = inode;
req->in.numargs = 1;
req->in.args[0].size = strlen(name) + 1;
req->in.args[0].value = name;
@@ -1157,7 +1035,7 @@ static struct file_operations fuse_dir_operations = {
.release = fuse_dir_release,
};
-static struct inode_operations fuse_file_inode_operations = {
+static struct inode_operations fuse_common_inode_operations = {
.setattr = fuse_setattr,
#ifdef KERNEL_2_6
.permission = fuse_permission,
@@ -1191,10 +1069,18 @@ static struct inode_operations fuse_symlink_inode_operations = {
#endif
};
-static struct dentry_operations fuse_dentry_operations = {
-#ifdef KERNEL_2_6
- .d_revalidate = fuse_dentry_revalidate,
-#else
- .d_revalidate = fuse_dentry_revalidate_2_4,
-#endif
-};
+void fuse_init_common(struct inode *inode)
+{
+ inode->i_op = &fuse_common_inode_operations;
+}
+
+void fuse_init_dir(struct inode *inode)
+{
+ inode->i_op = &fuse_dir_inode_operations;
+ inode->i_fop = &fuse_dir_operations;
+}
+
+void fuse_init_symlink(struct inode *inode)
+{
+ inode->i_op = &fuse_symlink_inode_operations;
+}
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 @@ static int fuse_open(struct inode *inode, struct file *file)
return err;
}
- /* Prevent concurrent unlink or rename */
- down(&inode->i_sem);
- err = -ERESTARTSYS;
req = fuse_get_request(fc);
if (!req)
- goto out;
+ return -ERESTARTSYS;
err = -ENOMEM;
ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL);
@@ -56,9 +53,10 @@ static int fuse_open(struct inode *inode, struct file *file)
}
memset(&inarg, 0, sizeof(inarg));
- inarg.flags = file->f_flags & ~O_EXCL;
+ inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
req->in.h.opcode = FUSE_OPEN;
req->in.h.nodeid = get_node_id(inode);
+ req->inode = inode;
req->in.numargs = 1;
req->in.args[0].size = sizeof(inarg);
req->in.args[0].value = &inarg;
@@ -85,8 +83,6 @@ static int fuse_open(struct inode *inode, struct file *file)
out_put_request:
fuse_put_request(fc, req);
- out:
- up(&inode->i_sem);
return err;
}
@@ -101,11 +97,11 @@ static int fuse_release(struct inode *inode, struct file *file)
inarg->flags = file->f_flags & ~O_EXCL;
req->in.h.opcode = FUSE_RELEASE;
req->in.h.nodeid = get_node_id(inode);
+ req->inode = inode;
req->in.numargs = 1;
req->in.args[0].size = sizeof(struct fuse_release_in);
req->in.args[0].value = inarg;
- request_send_nonint(fc, req, 1);
- fuse_put_request(fc, req);
+ request_send_background(fc, req);
kfree(ff);
/* Return value is ignored by VFS */
@@ -132,10 +128,12 @@ static int fuse_flush(struct file *file)
inarg.fh = ff->fh;
req->in.h.opcode = FUSE_FLUSH;
req->in.h.nodeid = get_node_id(inode);
+ req->inode = inode;
+ req->file = file;
req->in.numargs = 1;
req->in.args[0].size = sizeof(inarg);
req->in.args[0].value = &inarg;
- request_send_nonint(fc, req, 0);
+ request_send_nonint(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
if (err == -ENOSYS) {
@@ -163,9 +161,11 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
memset(&inarg, 0, sizeof(inarg));
inarg.fh = ff->fh;
- inarg.datasync = datasync;
+ inarg.fsync_flags = datasync ? 1 : 0;
req->in.h.opcode = FUSE_FSYNC;
req->in.h.nodeid = get_node_id(inode);
+ req->inode = inode;
+ req->file = file;
req->in.numargs = 1;
req->in.args[0].size = sizeof(inarg);
req->in.args[0].value = &inarg;
@@ -192,6 +192,8 @@ static ssize_t fuse_send_read(struct fuse_req *req, struct file *file,
inarg.size = count;
req->in.h.opcode = FUSE_READ;
req->in.h.nodeid = get_node_id(inode);
+ req->inode = inode;
+ req->file = file;
req->in.numargs = 1;
req->in.args[0].size = sizeof(struct fuse_read_in);
req->in.args[0].value = &inarg;
@@ -199,19 +201,19 @@ static ssize_t fuse_send_read(struct fuse_req *req, struct file *file,
req->out.argvar = 1;
req->out.numargs = 1;
req->out.args[0].size = count;
- request_send_nonint(fc, req, 0);
+ request_send_nonint(fc, req);
return req->out.args[0].size;
}
static int fuse_readpage(struct file *file, struct page *page)
{
- int err;
struct inode *inode = page->mapping->host;
struct fuse_conn *fc = get_fuse_conn(inode);
loff_t pos = (loff_t) page->index << PAGE_CACHE_SHIFT;
struct fuse_req *req = fuse_get_request_nonint(fc);
+ int err = -EINTR;
if (!req)
- return -EINTR;
+ goto out;
req->out.page_zeroing = 1;
req->num_pages = 1;
@@ -221,6 +223,7 @@ static int fuse_readpage(struct file *file, struct page *page)
fuse_put_request(fc, req);
if (!err)
SetPageUptodate(page);
+ out:
unlock_page(page);
return err;
}
@@ -261,9 +264,10 @@ static int fuse_readpages_fill(void *_data, struct page *page)
(req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_read ||
req->pages[req->num_pages - 1]->index + 1 != page->index)) {
int err = fuse_send_readpages(req, data->file, inode);
- if (err)
+ if (err) {
+ unlock_page(page);
return err;
-
+ }
fuse_reset_request(req);
}
req->pages[req->num_pages] = page;
@@ -278,7 +282,6 @@ static int fuse_readpages(struct file *file, struct address_space *mapping,
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_readpages_data data;
int err;
-
data.file = file;
data.inode = inode;
data.req = fuse_get_request_nonint(fc);
@@ -287,7 +290,7 @@ static int fuse_readpages(struct file *file, struct address_space *mapping,
err = read_cache_pages(mapping, pages, fuse_readpages_fill, &data);
if (!err && data.req->num_pages)
- fuse_send_readpages(data.req, file, inode);
+ err = fuse_send_readpages(data.req, file, inode);
fuse_put_request(fc, data.req);
return err;
}
@@ -400,12 +403,13 @@ static ssize_t fuse_send_write(struct fuse_req *req, struct file *file,
struct fuse_write_out outarg;
memset(&inarg, 0, sizeof(struct fuse_write_in));
- inarg.writepage = 0;
inarg.fh = ff->fh;
inarg.offset = pos;
inarg.size = count;
req->in.h.opcode = FUSE_WRITE;
req->in.h.nodeid = get_node_id(inode);
+ req->inode = inode;
+ req->file = file;
req->in.argpages = 1;
req->in.numargs = 2;
req->in.args[0].size = sizeof(struct fuse_write_in);
@@ -414,7 +418,7 @@ static ssize_t fuse_send_write(struct fuse_req *req, struct file *file,
req->out.numargs = 1;
req->out.args[0].size = sizeof(struct fuse_write_out);
req->out.args[0].value = &outarg;
- request_send_nonint(fc, req, 0);
+ request_send_nonint(fc, req);
return outarg.size;
}
@@ -584,6 +588,7 @@ static ssize_t fuse_file_write(struct file *file, const char __user *buf,
if (fc->flags & FUSE_DIRECT_IO) {
ssize_t res;
+ /* Don't allow parallel writes to the same file */
down(&inode->i_sem);
res = fuse_direct_io(file, buf, count, ppos, 1);
up(&inode->i_sem);
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 @@ static inline void set_page_dirty_lock(struct page *page)
unlock_page(page);
}
#endif
-/* Max number of pages that can be used in a single read request */
+/** Max number of pages that can be used in a single read request */
#define FUSE_MAX_PAGES_PER_REQ 32
-/* If more requests are outstanding, then the operation will block */
+/** If more requests are outstanding, then the operation will block */
#define FUSE_MAX_OUTSTANDING 10
/** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem
@@ -99,7 +101,7 @@ static inline void set_page_dirty_lock(struct page *page)
struct fuse_inode {
/** Unique ID, which identifies the inode between userspace
* and kernel */
- unsigned long nodeid;
+ u64 nodeid;
/** The request used for sending the FORGET message */
struct fuse_req *forget_req;
@@ -114,7 +116,7 @@ struct fuse_file {
struct fuse_req *release_req;
/** File handle used by userspace */
- unsigned long fh;
+ u64 fh;
};
/** One input argument of a request */
@@ -189,6 +191,9 @@ struct fuse_req {
/** The request was interrupted */
unsigned interrupted:1;
+ /** Request is sent in the background */
+ unsigned background:1;
+
/** Data is being copied to/from the request */
unsigned locked:1;
@@ -221,6 +226,15 @@ struct fuse_req {
/** offset of data on first page */
unsigned page_offset;
+
+ /** Inode used in the request */
+ struct inode *inode;
+
+ /** Second inode used in the request (or NULL) */
+ struct inode *inode2;
+
+ /** File used in the request (or NULL) */
+ struct file *file;
};
/**
@@ -259,7 +273,11 @@ struct fuse_conn {
struct list_head processing;
/** Controls the maximum number of outstanding requests */
- struct semaphore unused_sem;
+ struct semaphore outstanding_sem;
+
+ /** This counts the number of outstanding requests if
+ outstanding_sem would go negative */
+ unsigned outstanding_debt;
/** The list of unused requests */
struct list_head unused_list;
@@ -320,7 +338,7 @@ static inline struct fuse_inode *get_fuse_inode(struct inode *inode)
return (struct fuse_inode *) (&inode[1]);
}
-static inline unsigned long get_node_id(struct inode *inode)
+static inline u64 get_node_id(struct inode *inode)
{
return get_fuse_inode(inode)->nodeid;
}
@@ -349,26 +367,37 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
int generation, struct fuse_attr *attr, int version);
/**
- * Lookup an inode by nodeid
- */
-#ifdef KERNEL_2_6
-struct inode *fuse_ilookup(struct super_block *sb, unsigned long nodeid);
-#else
-struct inode *fuse_ilookup(struct super_block *sb, ino_t ino, unsigned long nodeid);
-#endif
-
-/**
* Send FORGET command
*/
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 @@ void request_send(struct fuse_conn *fc, struct fuse_req *req);
/**
* Send a request (synchronous, non-interruptible except by SIGKILL)
- *
- * If background is non-zero and SIGKILL is received still send
- * request asynchronously
*/
-void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req,
- int background);
+void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req);
/**
* Send a request with no reply
@@ -444,6 +469,11 @@ void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req,
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 @@ enum fuse_opcode {
#define FUSE_XATTR_SIZE_MAX 4096
struct fuse_entry_out {
- unsigned long nodeid; /* Inode ID */
- unsigned long generation; /* Inode generation: nodeid:gen must
- be unique for the fs's lifetime */
- unsigned long entry_valid; /* Cache timeout for the name */
- unsigned long entry_valid_nsec;
- unsigned long attr_valid; /* Cache timeout for the attributes */
- unsigned long attr_valid_nsec;
+ __u64 nodeid; /* Inode ID */
+ __u64 generation; /* Inode generation: nodeid:gen must
+ be unique for the fs's lifetime */
+ __u64 entry_valid; /* Cache timeout for the name */
+ __u64 attr_valid; /* Cache timeout for the attributes */
+ __u32 entry_valid_nsec;
+ __u32 attr_valid_nsec;
struct fuse_attr attr;
};
struct fuse_forget_in {
- int version;
+ __u64 version;
};
struct fuse_attr_out {
- unsigned long attr_valid; /* Cache timeout for the attributes */
- unsigned long attr_valid_nsec;
+ __u64 attr_valid; /* Cache timeout for the attributes */
+ __u32 attr_valid_nsec;
+ __u32 dummy;
struct fuse_attr attr;
};
struct fuse_getdir_out {
- int fd;
+ __u32 fd;
};
struct fuse_mknod_in {
- unsigned int mode;
- unsigned int rdev;
+ __u32 mode;
+ __u32 rdev;
};
struct fuse_mkdir_in {
- unsigned int mode;
+ __u32 mode;
};
struct fuse_rename_in {
- unsigned long newdir;
+ __u64 newdir;
};
struct fuse_link_in {
- unsigned long newdir;
+ __u64 newdir;
};
struct fuse_setattr_in {
+ __u32 valid;
struct fuse_attr attr;
- unsigned int valid;
};
struct fuse_open_in {
- unsigned int flags;
+ __u32 flags;
};
struct fuse_open_out {
- unsigned long fh;
- unsigned int _open_flags;
+ __u64 fh;
+ __u32 open_flags;
};
struct fuse_release_in {
- unsigned long fh;
- unsigned int flags;
+ __u64 fh;
+ __u32 flags;
};
struct fuse_flush_in {
- unsigned long fh;
- unsigned int _nref;
+ __u64 fh;
+ __u32 flush_flags;
};
struct fuse_read_in {
- unsigned long fh;
- unsigned long long offset;
- unsigned int size;
+ __u64 fh;
+ __u64 offset;
+ __u32 size;
};
struct fuse_write_in {
- int writepage;
- unsigned long fh;
- unsigned long long offset;
- unsigned int size;
+ __u64 fh;
+ __u64 offset;
+ __u32 size;
+ __u32 write_flags;
};
struct fuse_write_out {
- unsigned int size;
+ __u32 size;
};
struct fuse_statfs_out {
@@ -181,45 +184,47 @@ struct fuse_statfs_out {
};
struct fuse_fsync_in {
- unsigned long fh;
- int datasync;
+ __u64 fh;
+ __u32 fsync_flags;
};
struct fuse_setxattr_in {
- unsigned int size;
- unsigned int flags;
+ __u32 size;
+ __u32 flags;
};
struct fuse_getxattr_in {
- unsigned int size;
+ __u32 size;
};
struct fuse_getxattr_out {
- unsigned int size;
+ __u32 size;
};
struct fuse_in_header {
- int unique;
- enum fuse_opcode opcode;
- unsigned long nodeid;
- unsigned int uid;
- unsigned int gid;
- unsigned int pid;
+ __u32 len;
+ __u32 opcode;
+ __u64 unique;
+ __u64 nodeid;
+ __u32 uid;
+ __u32 gid;
+ __u32 pid;
};
struct fuse_out_header {
- int unique;
- int error;
+ __u32 len;
+ __s32 error;
+ __u64 unique;
};
struct fuse_dirent {
- unsigned long ino;
- unsigned short namelen;
- unsigned char type;
- char name[256];
+ __u64 ino;
+ __u32 namelen;
+ __u32 type;
+ char name[0];
};
-#define FUSE_NAME_OFFSET ((unsigned int) ((struct fuse_dirent *) 0)->name)
-#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(long) - 1) & ~(sizeof(long) - 1))
+#define FUSE_NAME_OFFSET ((unsigned) ((struct fuse_dirent *) 0)->name)
+#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1))
#define FUSE_DIRENT_SIZE(d) \
FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
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 @@ static void fuse_clear_inode(struct inode *inode)
}
}
+void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
+{
+ if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size)
+#ifdef KERNEL_2_6
+ invalidate_inode_pages(inode->i_mapping);
+#else
+ invalidate_inode_pages(inode);
+#endif
+
+ inode->i_ino = attr->ino;
+ inode->i_mode = (inode->i_mode & S_IFMT) + (attr->mode & 07777);
+ inode->i_nlink = attr->nlink;
+ inode->i_uid = attr->uid;
+ inode->i_gid = attr->gid;
+ i_size_write(inode, attr->size);
+ inode->i_blksize = PAGE_CACHE_SIZE;
+ inode->i_blocks = attr->blocks;
+#ifdef KERNEL_2_6
+ inode->i_atime.tv_sec = attr->atime;
+ inode->i_atime.tv_nsec = attr->atimensec;
+ inode->i_mtime.tv_sec = attr->mtime;
+ inode->i_mtime.tv_nsec = attr->mtimensec;
+ inode->i_ctime.tv_sec = attr->ctime;
+ inode->i_ctime.tv_nsec = attr->ctimensec;
+#else
+ inode->i_atime = attr->atime;
+ inode->i_mtime = attr->mtime;
+ inode->i_ctime = attr->ctime;
+#endif
+}
+
+static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
+{
+ inode->i_mode = attr->mode & S_IFMT;
+ i_size_write(inode, attr->size);
+ if (S_ISREG(inode->i_mode)) {
+ fuse_init_common(inode);
+ fuse_init_file_inode(inode);
+ } else if (S_ISDIR(inode->i_mode))
+ fuse_init_dir(inode);
+ else if (S_ISLNK(inode->i_mode))
+ fuse_init_symlink(inode);
+ else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
+ S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
+ fuse_init_common(inode);
+ init_special_inode(inode, inode->i_mode,
+ new_decode_dev(attr->rdev));
+ } else {
+ /* Don't let user create weird files */
+ inode->i_mode = S_IFREG;
+ fuse_init_common(inode);
+ fuse_init_file_inode(inode);
+ }
+}
+
+#ifdef KERNEL_2_6
+static int fuse_inode_eq(struct inode *inode, void *_nodeidp)
+{
+ unsigned long nodeid = *(unsigned long *) _nodeidp;
+ if (get_node_id(inode) == nodeid)
+ return 1;
+ else
+ return 0;
+}
+
+static int fuse_inode_set(struct inode *inode, void *_nodeidp)
+{
+ unsigned long nodeid = *(unsigned long *) _nodeidp;
+ get_fuse_inode(inode)->nodeid = nodeid;
+ return 0;
+}
+
+struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
+ int generation, struct fuse_attr *attr, int version)
+{
+ struct inode *inode;
+ struct fuse_conn *fc = get_fuse_conn_super(sb);
+ int retried = 0;
+
+ retry:
+ inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid);
+ if (!inode)
+ return NULL;
+
+ if ((inode->i_state & I_NEW)) {
+ inode->i_generation = generation;
+ inode->i_data.backing_dev_info = &fc->bdi;
+ fuse_init_inode(inode, attr);
+ unlock_new_inode(inode);
+ } else if ((inode->i_mode ^ attr->mode) & S_IFMT) {
+ BUG_ON(retried);
+ /* Inode has changed type, any I/O on the old should fail */
+ make_bad_inode(inode);
+ iput(inode);
+ retried = 1;
+ goto retry;
+ }
+
+ fuse_change_attributes(inode, attr);
+ inode->i_version = version;
+ return inode;
+}
+#else
+static int fuse_inode_eq(struct inode *inode, unsigned long ino, void *_nodeidp){
+ unsigned long nodeid = *(unsigned long *) _nodeidp;
+ if (inode->u.generic_ip && get_node_id(inode) == nodeid)
+ return 1;
+ else
+ return 0;
+}
+
+struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
+ int generation, struct fuse_attr *attr, int version)
+{
+ struct inode *inode;
+ int retried = 0;
+
+ retry:
+ inode = iget4(sb, attr->ino, fuse_inode_eq, &nodeid);
+ if (!inode)
+ return NULL;
+
+ if (!inode->u.generic_ip) {
+ get_fuse_inode(inode)->nodeid = nodeid;
+ inode->u.generic_ip = inode;
+ inode->i_generation = generation;
+ fuse_init_inode(inode, attr);
+ } else if ((inode->i_mode ^ attr->mode) & S_IFMT) {
+ BUG_ON(retried);
+ /* Inode has changed type, any I/O on the old should fail */
+ remove_inode_hash(inode);
+ make_bad_inode(inode);
+ iput(inode);
+ retried = 1;
+ goto retry;
+ }
+
+ fuse_change_attributes(inode, attr);
+ inode->i_version = version;
+ return inode;
+}
+#endif
+
static void fuse_put_super(struct super_block *sb)
{
struct fuse_conn *fc = get_fuse_conn_super(sb);
spin_lock(&fuse_lock);
+ mount_count --;
fc->sb = NULL;
fc->uid = 0;
fc->flags = 0;
@@ -329,7 +478,7 @@ static struct fuse_conn *new_conn(void)
INIT_LIST_HEAD(&fc->pending);
INIT_LIST_HEAD(&fc->processing);
INIT_LIST_HEAD(&fc->unused_list);
- sema_init(&fc->unused_sem, FUSE_MAX_OUTSTANDING);
+ sema_init(&fc->outstanding_sem, FUSE_MAX_OUTSTANDING);
for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) {
struct fuse_req *req = fuse_request_alloc();
if (!req) {
@@ -392,7 +541,7 @@ static struct dentry *fuse_get_dentry(struct super_block *sb, void *vobjp)
if (nodeid == 0)
return ERR_PTR(-ESTALE);
- inode = fuse_ilookup(sb, nodeid);
+ inode = ilookup5(sb, nodeid, fuse_inode_eq, &nodeid);
if (!inode || inode->i_generation != generation)
return ERR_PTR(-ESTALE);
@@ -449,12 +598,24 @@ static struct super_operations fuse_super_operations = {
.show_options = fuse_show_options,
};
+static int inc_mount_count(void)
+{
+ int success = 0;
+ spin_lock(&fuse_lock);
+ mount_count ++;
+ if (mount_max == -1 || mount_count <= mount_max)
+ success = 1;
+ spin_unlock(&fuse_lock);
+ return success;
+}
+
static int fuse_read_super(struct super_block *sb, void *data, int silent)
{
struct fuse_conn *fc;
struct inode *root;
struct fuse_mount_data d;
struct file *file;
+ int err;
if (!parse_fuse_opt((char *) data, &d))
return -EINVAL;
@@ -493,6 +654,11 @@ static int fuse_read_super(struct super_block *sb, void *data, int silent)
*get_fuse_conn_super_p(sb) = fc;
+ err = -ENFILE;
+ if (!inc_mount_count() && current->uid != 0)
+ goto err;
+
+ err = -ENOMEM;
root = get_root_inode(sb, d.rootmode);
if (root == NULL)
goto err;
@@ -507,11 +673,12 @@ static int fuse_read_super(struct super_block *sb, void *data, int silent)
err:
spin_lock(&fuse_lock);
+ mount_count --;
fc->sb = NULL;
fuse_release_conn(fc);
spin_unlock(&fuse_lock);
*get_fuse_conn_super_p(sb) = NULL;
- return -EINVAL;
+ return err;
}
#ifdef KERNEL_2_6