aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/dev.c151
-rw-r--r--kernel/fuse_i.h8
-rw-r--r--kernel/inode.c79
3 files changed, 136 insertions, 102 deletions
diff --git a/kernel/dev.c b/kernel/dev.c
index 71d306a..0c601d2 100644
--- a/kernel/dev.c
+++ b/kernel/dev.c
@@ -12,13 +12,21 @@
#include <linux/proc_fs.h>
#include <linux/file.h>
-/* If more requests are outstanding, then the operation will block */
-#define MAX_OUTSTANDING 10
-
static struct proc_dir_entry *proc_fs_fuse;
struct proc_dir_entry *proc_fuse_dev;
static kmem_cache_t *fuse_req_cachep;
+static inline struct fuse_conn *fuse_get_conn(struct file *file)
+{
+ struct fuse_conn *fc;
+ spin_lock(&fuse_lock);
+ fc = (struct fuse_conn *) file->private_data;
+ if (fc && !fc->sb)
+ fc = NULL;
+ spin_unlock(&fuse_lock);
+ return fc;
+}
+
struct fuse_req *fuse_request_alloc(void)
{
struct fuse_req *req;
@@ -211,13 +219,15 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
{
req->issync = 0;
+ spin_lock(&fuse_lock);
if (fc->file) {
- spin_lock(&fuse_lock);
list_add_tail(&req->list, &fc->pending);
wake_up(&fc->waitq);
spin_unlock(&fuse_lock);
- } else
+ } else {
+ spin_unlock(&fuse_lock);
fuse_put_request(fc, req);
+ }
}
void request_send_nonblock(struct fuse_conn *fc, struct fuse_req *req,
@@ -244,7 +254,7 @@ static void request_wait(struct fuse_conn *fc)
DECLARE_WAITQUEUE(wait, current);
add_wait_queue_exclusive(&fc->waitq, &wait);
- while (fc->sb != NULL && list_empty(&fc->pending)) {
+ while (fc->sb && list_empty(&fc->pending)) {
set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current))
break;
@@ -298,18 +308,25 @@ static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes,
loff_t *off)
{
ssize_t ret;
- struct fuse_conn *fc = DEV_FC(file);
+ struct fuse_conn *fc;
struct fuse_req *req = NULL;
spin_lock(&fuse_lock);
+ fc = (struct fuse_conn *) file->private_data;
+ if (!fc) {
+ spin_unlock(&fuse_lock);
+ return -EPERM;
+ }
request_wait(fc);
- if (fc->sb != NULL && !list_empty(&fc->pending)) {
+ if (!fc->sb)
+ fc = NULL;
+ else if (!list_empty(&fc->pending)) {
req = list_entry(fc->pending.next, struct fuse_req, list);
list_del_init(&req->list);
req->locked = 1;
}
spin_unlock(&fuse_lock);
- if (fc->sb == NULL)
+ if (!fc)
return -ENODEV;
if (req == NULL)
return -EINTR;
@@ -437,18 +454,26 @@ static inline int copy_out_header(struct fuse_out_header *oh, const char *buf,
static int fuse_invalidate(struct fuse_conn *fc, struct fuse_user_header *uh)
{
- struct inode *inode = fuse_ilookup(fc, uh->ino, uh->nodeid);
- if (!inode)
- return -ENOENT;
- fuse_sync_inode(inode);
+ int err;
+ down(&fc->sb_sem);
+ err = -ENODEV;
+ if (fc->sb) {
+ struct inode *inode = fuse_ilookup(fc, uh->ino, uh->nodeid);
+ err = -ENOENT;
+ if (inode) {
+ fuse_sync_inode(inode);
#ifdef KERNEL_2_6
- invalidate_inode_pages(inode->i_mapping);
+ invalidate_inode_pages(inode->i_mapping);
#else
- invalidate_inode_pages(inode);
+ invalidate_inode_pages(inode);
#endif
+ iput(inode);
+ err = 0;
+ }
+ }
+ up(&fc->sb_sem);
- iput(inode);
- return 0;
+ return err;
}
static int fuse_user_request(struct fuse_conn *fc, const char *buf,
@@ -481,12 +506,12 @@ static ssize_t fuse_dev_write(struct file *file, const char *buf,
size_t nbytes, loff_t *off)
{
int err;
- struct fuse_conn *fc = DEV_FC(file);
+ struct fuse_conn *fc = fuse_get_conn(file);
struct fuse_req *req;
struct fuse_out_header oh;
-
- if (!fc->sb)
- return -EPERM;
+
+ if (!fc)
+ return -ENODEV;
err = copy_out_header(&oh, buf, nbytes);
if (err)
@@ -538,11 +563,11 @@ static ssize_t fuse_dev_write(struct file *file, const char *buf,
static unsigned int fuse_dev_poll(struct file *file, poll_table *wait)
{
- struct fuse_conn *fc = DEV_FC(file);
+ struct fuse_conn *fc = fuse_get_conn(file);
unsigned int mask = POLLOUT | POLLWRNORM;
- if (!fc->sb)
- return -EPERM;
+ if (!fc)
+ return -ENODEV;
poll_wait(file, &fc->waitq, wait);
@@ -554,70 +579,6 @@ static unsigned int fuse_dev_poll(struct file *file, poll_table *wait)
return mask;
}
-static void free_conn(struct fuse_conn *fc)
-{
- while (!list_empty(&fc->unused_list)) {
- struct fuse_req *req;
- req = list_entry(fc->unused_list.next, struct fuse_req, list);
- list_del(&req->list);
- fuse_request_free(req);
- }
- kfree(fc);
-}
-
-/* Must be called with the fuse lock held */
-void fuse_release_conn(struct fuse_conn *fc)
-{
- if (fc->sb == NULL && fc->file == NULL) {
- free_conn(fc);
- }
-}
-
-static struct fuse_conn *new_conn(void)
-{
- struct fuse_conn *fc;
-
- fc = kmalloc(sizeof(*fc), GFP_KERNEL);
- if (fc != NULL) {
- int i;
- memset(fc, 0, sizeof(*fc));
- fc->sb = NULL;
- fc->file = NULL;
- fc->flags = 0;
- fc->uid = 0;
- init_waitqueue_head(&fc->waitq);
- INIT_LIST_HEAD(&fc->pending);
- INIT_LIST_HEAD(&fc->processing);
- INIT_LIST_HEAD(&fc->unused_list);
- sema_init(&fc->unused_sem, MAX_OUTSTANDING);
- for (i = 0; i < MAX_OUTSTANDING; i++) {
- struct fuse_req *req = fuse_request_alloc();
- if (!req) {
- free_conn(fc);
- return NULL;
- }
- req->preallocated = 1;
- list_add(&req->list, &fc->unused_list);
- }
- fc->reqctr = 1;
- }
- return fc;
-}
-
-static int fuse_dev_open(struct inode *inode, struct file *file)
-{
- struct fuse_conn *fc;
-
- fc = new_conn();
- if (!fc)
- return -ENOMEM;
-
- fc->file = file;
- file->private_data = fc;
-
- return 0;
-}
-
static void end_requests(struct fuse_conn *fc, struct list_head *head)
{
while (!list_empty(head)) {
@@ -640,13 +601,16 @@ static void end_requests(struct fuse_conn *fc, struct list_head *head)
static int fuse_dev_release(struct inode *inode, struct file *file)
{
- struct fuse_conn *fc = DEV_FC(file);
+ struct fuse_conn *fc;
spin_lock(&fuse_lock);
- fc->file = NULL;
- end_requests(fc, &fc->pending);
- end_requests(fc, &fc->processing);
- fuse_release_conn(fc);
+ fc = (struct fuse_conn *) file->private_data;
+ if (fc) {
+ fc->file = NULL;
+ end_requests(fc, &fc->pending);
+ end_requests(fc, &fc->processing);
+ fuse_release_conn(fc);
+ }
spin_unlock(&fuse_lock);
return 0;
}
@@ -656,7 +620,6 @@ static struct file_operations fuse_dev_operations = {
.read = fuse_dev_read,
.write = fuse_dev_write,
.poll = fuse_dev_poll,
- .open = fuse_dev_open,
.release = fuse_dev_release,
};
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
index 3c18975..40dedb2 100644
--- a/kernel/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -49,6 +49,10 @@
#define FUSE_MAX_PAGES_PER_REQ 32
+/* If more requests are outstanding, then the operation will block */
+#define FUSE_MAX_OUTSTANDING 10
+
+
/** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem
module will check permissions based on the file mode. Otherwise no
permission checking is done in the kernel */
@@ -225,6 +229,9 @@ struct fuse_conn {
/** Controls the maximum number of outstanding requests */
struct semaphore unused_sem;
+ /** Semaphore protecting the super block from going away */
+ struct semaphore sb_sem;
+
/** The list of unused requests */
struct list_head unused_list;
@@ -261,7 +268,6 @@ struct fuse_getdir_out_i {
#define SB_FC(sb) ((sb)->u.generic_sbp)
#endif
#define INO_FC(inode) SB_FC((inode)->i_sb)
-#define DEV_FC(file) ((file)->private_data)
#define INO_FI(i) ((struct fuse_inode *) (((struct inode *)(i))+1))
diff --git a/kernel/inode.c b/kernel/inode.c
index 6ad42ee..591a4df 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -126,8 +126,10 @@ static void fuse_put_super(struct super_block *sb)
{
struct fuse_conn *fc = SB_FC(sb);
+ down(&fc->sb_sem);
spin_lock(&fuse_lock);
fc->sb = NULL;
+ up(&fc->sb_sem);
fc->uid = 0;
fc->flags = 0;
/* Flush all readers on this fs */
@@ -308,22 +310,85 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt)
return 0;
}
+static void free_conn(struct fuse_conn *fc)
+{
+ while (!list_empty(&fc->unused_list)) {
+ struct fuse_req *req;
+ req = list_entry(fc->unused_list.next, struct fuse_req, list);
+ list_del(&req->list);
+ fuse_request_free(req);
+ }
+ kfree(fc);
+}
+
+/* Must be called with the fuse lock held */
+void fuse_release_conn(struct fuse_conn *fc)
+{
+ if (!fc->sb && !fc->file)
+ free_conn(fc);
+}
+
+
+static struct fuse_conn *new_conn(void)
+{
+ struct fuse_conn *fc;
+
+ fc = kmalloc(sizeof(*fc), GFP_KERNEL);
+ if (fc != NULL) {
+ int i;
+ memset(fc, 0, sizeof(*fc));
+ fc->sb = NULL;
+ fc->file = NULL;
+ fc->flags = 0;
+ fc->uid = 0;
+ init_waitqueue_head(&fc->waitq);
+ 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->sb_sem, 1);
+ for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) {
+ struct fuse_req *req = fuse_request_alloc();
+ if (!req) {
+ free_conn(fc);
+ return NULL;
+ }
+ req->preallocated = 1;
+ list_add(&req->list, &fc->unused_list);
+ }
+ fc->reqctr = 1;
+ }
+ return fc;
+}
+
static struct fuse_conn *get_conn(struct file *file, struct super_block *sb)
{
struct fuse_conn *fc;
struct inode *ino;
ino = file->f_dentry->d_inode;
- if (!ino || !proc_fuse_dev || proc_fuse_dev->low_ino != ino->i_ino) {
+ if (!ino || !proc_fuse_dev ||
+ strcmp(ino->i_sb->s_type->name, "proc") != 0 ||
+ proc_fuse_dev->low_ino != ino->i_ino) {
printk("FUSE: bad communication file descriptor\n");
return NULL;
}
- fc = file->private_data;
- if (fc->sb != NULL) {
- printk("fuse_read_super: connection already mounted\n");
+ fc = new_conn();
+ if (fc == NULL) {
+ printk("FUSE: failed to allocate connection data\n");
return NULL;
}
- fc->sb = sb;
+ spin_lock(&fuse_lock);
+ if (file->private_data) {
+ printk("fuse_read_super: connection already mounted\n");
+ free_conn(fc);
+ fc = NULL;
+ } else {
+ file->private_data = fc;
+ fc->sb = sb;
+ fc->file = file;
+ }
+ spin_unlock(&fuse_lock);
return fc;
}
@@ -407,9 +472,7 @@ static int fuse_read_super(struct super_block *sb, void *data, int silent)
if (!file)
return -EINVAL;
- spin_lock(&fuse_lock);
fc = get_conn(file, sb);
- spin_unlock(&fuse_lock);
fput(file);
if (fc == NULL)
return -EINVAL;
@@ -438,8 +501,10 @@ static int fuse_read_super(struct super_block *sb, void *data, int silent)
return 0;
err:
+ down(&fc->sb_sem);
spin_lock(&fuse_lock);
fc->sb = NULL;
+ up(&fc->sb_sem);
fuse_release_conn(fc);
spin_unlock(&fuse_lock);
SB_FC(sb) = NULL;