diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/dev.c | 149 | ||||
-rw-r--r-- | kernel/dir.c | 322 | ||||
-rw-r--r-- | kernel/file.c | 45 | ||||
-rw-r--r-- | kernel/fuse_i.h | 72 | ||||
-rw-r--r-- | kernel/fuse_kernel.h | 151 | ||||
-rw-r--r-- | kernel/inode.c | 173 |
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 |