aboutsummaryrefslogtreecommitdiffstats
path: root/dev.c
blob: 159d8d6148e33fa67d90cd8f78fc62d21c72dbdc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/*
    FUSE: Filesystem in Userspace
    Copyright (C) 2001  Miklos Szeredi (mszeredi@inf.bme.hu)

    This program can be distributed under the terms of the GNU GPL.
    See the file COPYING.
*/

#include "fuse_i.h"

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/poll.h>
#include <linux/proc_fs.h>

static struct proc_dir_entry *proc_fs_fuse;
struct proc_dir_entry *proc_fuse_dev;

static struct fuse_req *request_wait(struct fuse_conn *fc)
{
	DECLARE_WAITQUEUE(wait, current);
	struct fuse_req *req;

	spin_lock(&fuse_lock);
	add_wait_queue(&fc->waitq, &wait);
	set_current_state(TASK_INTERRUPTIBLE);
	while(list_empty(&fc->pending)) {
		if(signal_pending(current))
			break;

		spin_unlock(&fuse_lock);
		schedule();
		spin_lock(&fuse_lock);
	}
	set_current_state(TASK_RUNNING);
	remove_wait_queue(&fc->waitq, &wait);

	if(list_empty(&fc->pending))
		return NULL;

	req = list_entry(fc->pending.next, struct fuse_req, list);
	list_del(&req->list);
	spin_unlock(&fuse_lock);

	return req;
}

static void request_processing(struct fuse_conn *fc, struct fuse_req *req)
{
	spin_lock(&fuse_lock);
	list_add_tail(&req->list, &fc->processing);
	fc->outstanding ++;
	spin_unlock(&fuse_lock);
}

static void request_free(struct fuse_req *req)
{
	kfree(req);
}

static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes,
			     loff_t *off)
{
	ssize_t res;
	struct fuse_conn *fc = file->private_data;
	struct fuse_req *req;

	printk(KERN_DEBUG "fuse_dev_read[%i]\n", fc->id);

	res = -ERESTARTSYS;
	req = request_wait(fc);
	if(req == NULL)
		goto err;

	res = -EIO;
	if(nbytes < req->size) {
		printk("fuse_dev_read: buffer too small (%i)\n", req->size);
		goto err_free_req;
	}
	
	res = -EFAULT;
	if(copy_to_user(buf, req->data, req->size))
		goto err_free_req;

	request_processing(fc, req);
	return req->size;

  err_free_req:
	request_free(req);
  err:
	return res;
}

static ssize_t fuse_dev_write(struct file *file, const char *buf,
				size_t nbytes, loff_t *off)
{
	struct fuse_conn *fc = file->private_data;

	printk(KERN_DEBUG "fuse_dev_write[%i] <%.*s>\n", fc->id, (int) nbytes,
	       buf);
	return nbytes;
}


static unsigned int fuse_dev_poll(struct file *file, poll_table *wait)
{
	struct fuse_conn *fc = file->private_data;

	printk(KERN_DEBUG "fuse_dev_poll[%i]\n", fc->id);
	return 0;
}

static struct fuse_conn *new_conn(void)
{
	static int connctr = 1;
	struct fuse_conn *fc;

	fc = kmalloc(sizeof(*fc), GFP_KERNEL);
	if(fc != NULL) {
		fc->sb = NULL;
		fc->file = NULL;
		init_waitqueue_head(&fc->waitq);
		INIT_LIST_HEAD(&fc->pending);
		INIT_LIST_HEAD(&fc->processing);
		fc->outstanding = 0;
		
		spin_lock(&fuse_lock);
		fc->id = connctr ++;
		spin_unlock(&fuse_lock);
	}
	return fc;
}

static int fuse_dev_open(struct inode *inode, struct file *file)
{
	struct fuse_conn *fc;

	printk(KERN_DEBUG "fuse_dev_open\n");

	fc = new_conn();
	if(!fc)
		return -ENOMEM;

	fc->file = file;
	file->private_data = fc;

	printk(KERN_DEBUG "new connection: %i\n", fc->id);

	return 0;
}

static int fuse_dev_release(struct inode *inode, struct file *file)
{
	struct fuse_conn *fc = file->private_data;

	printk(KERN_DEBUG "fuse_dev_release[%i]\n", fc->id);

	spin_lock(&fuse_lock);
	fc->file = NULL;
	fuse_release_conn(fc);
	spin_unlock(&fuse_lock);
	return 0;
}

static struct file_operations fuse_dev_operations = {
	owner:		THIS_MODULE,
	read:		fuse_dev_read,
	write:		fuse_dev_write,
	poll:		fuse_dev_poll,
	open:		fuse_dev_open,
	release:	fuse_dev_release,
};

int fuse_dev_init()
{
	int res;

	proc_fs_fuse = NULL;
	proc_fuse_dev = NULL;

	res = -EIO;
	proc_fs_fuse = proc_mkdir("fuse", proc_root_fs);
	if(!proc_fs_fuse) {
		printk("fuse: failed to create directory in /proc/fs\n");
		goto err;
	}

	proc_fs_fuse->owner = THIS_MODULE;
	proc_fuse_dev = create_proc_entry("dev", S_IFSOCK | S_IRUGO | S_IWUGO,
					  proc_fs_fuse);
	if(!proc_fuse_dev) {
		printk("fuse: failed to create entry in /proc/fs/fuse\n");
		goto err;
	}

	proc_fuse_dev->proc_fops = &fuse_dev_operations;

	return 0;

  err:
	fuse_dev_cleanup();
	return res;

}

void fuse_dev_cleanup()
{
	if(proc_fs_fuse) {
		remove_proc_entry("dev", proc_fs_fuse);
		remove_proc_entry("fuse", proc_root_fs);
	}
}

/* 
 * Local Variables:
 * indent-tabs-mode: t
 * c-basic-offset: 8
 * End:
 */