aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorGravatar Miklos Szeredi <miklos@szeredi.hu>2001-10-28 19:44:14 +0000
committerGravatar Miklos Szeredi <miklos@szeredi.hu>2001-10-28 19:44:14 +0000
commit85c74fcdfd9e67d411c3e1734b34effd0d73fa4d (patch)
tree908e39d3e0b84bd733261cdde16ef6ae707f2352 /kernel
parent90d8bef61c8c40472ddfb1aafeeb6473ec51a053 (diff)
x
Diffstat (limited to 'kernel')
-rw-r--r--kernel/Makefile16
-rw-r--r--kernel/dev.c440
-rw-r--r--kernel/dir.c384
-rw-r--r--kernel/fuse_i.h169
-rw-r--r--kernel/inode.c228
-rw-r--r--kernel/util.c61
6 files changed, 1298 insertions, 0 deletions
diff --git a/kernel/Makefile b/kernel/Makefile
new file mode 100644
index 0000000..f7769d5
--- /dev/null
+++ b/kernel/Makefile
@@ -0,0 +1,16 @@
+CC = gcc
+CFLAGS = -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -pipe
+CPPFLAGS = -I /lib/modules/`uname -r`/build/include/ -D__KERNEL__ -DMODULE -D_LOOSE_KERNEL_NAMES -I../include
+
+
+all: fuse.o
+
+fuse_objs = dev.o inode.o dir.o util.o
+
+fuse.o: $(fuse_objs)
+ ld -r -o fuse.o $(fuse_objs)
+
+
+clean:
+ rm -f *.o
+ rm -f *~
diff --git a/kernel/dev.c b/kernel/dev.c
new file mode 100644
index 0000000..a91a176
--- /dev/null
+++ b/kernel/dev.c
@@ -0,0 +1,440 @@
+/*
+ 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>
+#include <linux/file.h>
+
+#define IHSIZE sizeof(struct fuse_in_header)
+#define OHSIZE sizeof(struct fuse_out_header)
+
+static struct proc_dir_entry *proc_fs_fuse;
+struct proc_dir_entry *proc_fuse_dev;
+
+static int request_wait_answer(struct fuse_req *req)
+{
+ int ret = 0;
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue(&req->waitq, &wait);
+ while(!list_empty(&req->list)) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if(signal_pending(current)) {
+ ret = -EINTR;
+ break;
+ }
+ spin_unlock(&fuse_lock);
+ schedule();
+ spin_lock(&fuse_lock);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&req->waitq, &wait);
+
+ return ret;
+}
+
+static int request_check(struct fuse_req *req, struct fuse_out *outp)
+{
+ struct fuse_out_header *oh;
+ unsigned int size;
+
+ if(!req->out)
+ return -ECONNABORTED;
+
+ oh = (struct fuse_out_header *) req->out;
+ size = req->outsize - OHSIZE;
+
+ if (oh->result <= -512 || oh->result > 0) {
+ printk("fuse: bad result\n");
+ return -EPROTO;
+ }
+
+ if(size > outp->argsize ||
+ (oh->result == 0 && !outp->argvar && size != outp->argsize) ||
+ (oh->result != 0 && size != 0)) {
+ printk("fuse: invalid argument length: %i (%i)\n", size,
+ req->opcode);
+ return -EPROTO;
+ }
+
+ memcpy(&outp->h, oh, OHSIZE);
+ outp->argsize = size;
+ if(size)
+ memcpy(outp->arg, req->out + OHSIZE, size);
+
+ return oh->result;
+}
+
+static void request_free(struct fuse_req *req)
+{
+ kfree(req->in);
+ kfree(req->out);
+ kfree(req);
+}
+
+
+static struct fuse_req *request_new(struct fuse_conn *fc, struct fuse_in *inp,
+ struct fuse_out *outp)
+{
+ struct fuse_req *req;
+
+ req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if(!req)
+ return NULL;
+
+ if(outp)
+ req->outsize = OHSIZE + outp->argsize;
+ else
+ req->outsize = 0;
+ req->out = NULL;
+
+ req->insize = IHSIZE + inp->argsize;
+ req->in = kmalloc(req->insize, GFP_KERNEL);
+ if(!req->in) {
+ request_free(req);
+ return NULL;
+ }
+ memcpy(req->in, &inp->h, IHSIZE);
+ if(inp->argsize)
+ memcpy(req->in + IHSIZE, inp->arg, inp->argsize);
+
+ req->opcode = inp->h.opcode;
+ init_waitqueue_head(&req->waitq);
+
+ return req;
+}
+
+/* If 'outp' is NULL then the request this is asynchronous */
+void request_send(struct fuse_conn *fc, struct fuse_in *inp,
+ struct fuse_out *outp)
+{
+ int ret;
+ struct fuse_in_header *ih;
+ struct fuse_req *req;
+
+ ret = -ENOMEM;
+ req = request_new(fc, inp, outp);
+ if(!req)
+ goto out;
+
+ spin_lock(&fuse_lock);
+ ret = -ENOTCONN;
+ if(fc->file == NULL)
+ goto out_unlock_free;
+
+ ih = (struct fuse_in_header *) req->in;
+ if(outp) {
+ do fc->reqctr++;
+ while(!fc->reqctr);
+ ih->unique = req->unique = fc->reqctr;
+ }
+ else
+ ih->unique = req->unique = 0;
+
+ list_add_tail(&req->list, &fc->pending);
+ wake_up(&fc->waitq);
+
+ /* Async reqests are freed in fuse_dev_read() */
+ if(!outp)
+ goto out_unlock;
+
+ ret = request_wait_answer(req);
+ list_del(&req->list);
+ if(!ret)
+ ret = request_check(req, outp);
+
+ out_unlock_free:
+ request_free(req);
+ out_unlock:
+ spin_unlock(&fuse_lock);
+ out:
+ if(outp)
+ outp->h.result = ret;
+}
+
+static int request_wait(struct fuse_conn *fc)
+{
+ int ret = 0;
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue(&fc->waitq, &wait);
+ while(list_empty(&fc->pending)) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if(signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ spin_unlock(&fuse_lock);
+ schedule();
+ spin_lock(&fuse_lock);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&fc->waitq, &wait);
+
+ return ret;
+}
+
+
+static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes,
+ loff_t *off)
+{
+ int ret;
+ struct fuse_conn *fc = file->private_data;
+ struct fuse_req *req;
+ char *tmpbuf;
+ unsigned int size;
+
+ if(fc->sb == NULL)
+ return -EPERM;
+
+ spin_lock(&fuse_lock);
+ ret = request_wait(fc);
+ if(ret)
+ goto err;
+
+ req = list_entry(fc->pending.next, struct fuse_req, list);
+ size = req->insize;
+ if(nbytes < size) {
+ printk("fuse_dev_read[%i]: buffer too small\n", fc->id);
+ ret = -EIO;
+ goto err;
+ }
+ tmpbuf = req->in;
+ req->in = NULL;
+
+ list_del(&req->list);
+ if(req->outsize)
+ list_add_tail(&req->list, &fc->processing);
+ else
+ request_free(req);
+ spin_unlock(&fuse_lock);
+
+ if(copy_to_user(buf, tmpbuf, size))
+ return -EFAULT;
+
+ return size;
+
+ err:
+ spin_unlock(&fuse_lock);
+ return ret;
+}
+
+static struct fuse_req *request_find(struct fuse_conn *fc, unsigned int unique)
+{
+ struct list_head *entry;
+ struct fuse_req *req = NULL;
+
+ list_for_each(entry, &fc->processing) {
+ struct fuse_req *tmp;
+ tmp = list_entry(entry, struct fuse_req, list);
+ if(tmp->unique == unique) {
+ req = tmp;
+ break;
+ }
+ }
+
+ return req;
+}
+
+static ssize_t fuse_dev_write(struct file *file, const char *buf,
+ size_t nbytes, loff_t *off)
+{
+ ssize_t ret;
+ struct fuse_conn *fc = file->private_data;
+ struct fuse_req *req;
+ char *tmpbuf;
+ struct fuse_out_header *oh;
+
+ if(!fc->sb)
+ return -EPERM;
+
+ ret = -EIO;
+ if(nbytes < OHSIZE || nbytes > OHSIZE + PAGE_SIZE) {
+ printk("fuse_dev_write[%i]: write is short or long\n", fc->id);
+ goto out;
+ }
+
+ ret = -ENOMEM;
+ tmpbuf = kmalloc(nbytes, GFP_KERNEL);
+ if(!tmpbuf)
+ goto out;
+
+ ret = -EFAULT;
+ if(copy_from_user(tmpbuf, buf, nbytes))
+ goto out_free;
+
+ spin_lock(&fuse_lock);
+ oh = (struct fuse_out_header *) tmpbuf;
+ req = request_find(fc, oh->unique);
+ if(req == NULL) {
+ ret = -ENOENT;
+ goto out_free_unlock;
+ }
+ list_del_init(&req->list);
+ if(req->opcode == FUSE_GETDIR) {
+ /* fget() needs to be done in this context */
+ struct fuse_getdir_out *arg;
+ arg = (struct fuse_getdir_out *) (tmpbuf + OHSIZE);
+ arg->file = fget(arg->fd);
+ }
+ req->out = tmpbuf;
+ req->outsize = nbytes;
+ tmpbuf = NULL;
+ ret = nbytes;
+ wake_up(&req->waitq);
+ out_free_unlock:
+ spin_unlock(&fuse_lock);
+ out_free:
+ kfree(tmpbuf);
+ out:
+ return ret;
+}
+
+
+static unsigned int fuse_dev_poll(struct file *file, poll_table *wait)
+{
+ struct fuse_conn *fc = file->private_data;
+ unsigned int mask = POLLOUT | POLLWRNORM;
+
+ if(!fc->sb)
+ return -EPERM;
+
+ poll_wait(file, &fc->waitq, wait);
+
+ spin_lock(&fuse_lock);
+ if (!list_empty(&fc->pending))
+ mask |= POLLIN | POLLRDNORM;
+ spin_unlock(&fuse_lock);
+
+ return mask;
+}
+
+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->reqctr = 1;
+ fc->cleared = NULL;
+ fc->numcleared = 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;
+
+ fc = new_conn();
+ if(!fc)
+ return -ENOMEM;
+
+ fc->file = file;
+ file->private_data = fc;
+
+ return 0;
+}
+
+static void end_requests(struct list_head *head)
+{
+ while(!list_empty(head)) {
+ struct fuse_req *req;
+ req = list_entry(head->next, struct fuse_req, list);
+ list_del_init(&req->list);
+ if(req->outsize)
+ wake_up(&req->waitq);
+ else
+ request_free(req);
+ }
+}
+
+static int fuse_dev_release(struct inode *inode, struct file *file)
+{
+ struct fuse_conn *fc = file->private_data;
+
+ spin_lock(&fuse_lock);
+ fc->file = NULL;
+ end_requests(&fc->pending);
+ end_requests(&fc->processing);
+ kfree(fc->cleared);
+ fc->cleared = NULL;
+ fc->numcleared = 0;
+ 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 ret;
+
+ proc_fs_fuse = NULL;
+ proc_fuse_dev = NULL;
+
+ ret = -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 ret;
+}
+
+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:
+ */
diff --git a/kernel/dir.c b/kernel/dir.c
new file mode 100644
index 0000000..92ddc55
--- /dev/null
+++ b/kernel/dir.c
@@ -0,0 +1,384 @@
+/*
+ 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/sched.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+
+static void change_attributes(struct inode *inode, struct fuse_attr *attr)
+{
+ inode->i_mode = attr->mode;
+ inode->i_nlink = attr->nlink;
+ inode->i_uid = attr->uid;
+ inode->i_gid = attr->gid;
+ inode->i_size = attr->size;
+ inode->i_blksize = attr->blksize;
+ inode->i_blocks = attr->blocks;
+ inode->i_atime = attr->atime;
+ inode->i_mtime = attr->mtime;
+ inode->i_ctime = attr->ctime;
+}
+
+static void init_inode(struct inode *inode, struct fuse_attr *attr)
+{
+ change_attributes(inode, attr);
+
+ if(S_ISREG(inode->i_mode))
+ fuse_file_init(inode);
+ else if(S_ISDIR(inode->i_mode))
+ fuse_dir_init(inode);
+ else if(S_ISLNK(inode->i_mode))
+ fuse_symlink_init(inode);
+ else {
+ fuse_special_init(inode);
+ init_special_inode(inode, inode->i_mode, attr->rdev);
+ }
+}
+
+
+static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
+{
+ struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
+ struct fuse_in in = FUSE_IN_INIT;
+ struct fuse_out out = FUSE_OUT_INIT;
+ struct fuse_lookup_out arg;
+ struct inode *inode;
+
+ in.h.opcode = FUSE_LOOKUP;
+ in.h.ino = dir->i_ino;
+ in.argsize = entry->d_name.len + 1;
+ in.arg = entry->d_name.name;
+ out.argsize = sizeof(arg);
+ out.arg = &arg;
+ request_send(fc, &in, &out);
+
+ if(out.h.result) {
+ /* Negative dentries are not hashed */
+ if(out.h.result == -ENOENT)
+ return NULL;
+ else
+ return ERR_PTR(out.h.result);
+ }
+
+ inode = iget(dir->i_sb, arg.ino);
+ if(!inode)
+ return ERR_PTR(-ENOMEM);
+
+ init_inode(inode, &arg.attr);
+ d_add(entry, inode);
+ return NULL;
+}
+
+static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
+ int rdev)
+{
+ struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
+ struct fuse_in in = FUSE_IN_INIT;
+ struct fuse_out out = FUSE_OUT_INIT;
+ struct fuse_mknod_in *inarg;
+ unsigned int insize;
+ struct fuse_mknod_out outarg;
+ struct inode *inode;
+
+ insize = offsetof(struct fuse_mknod_in, name) + entry->d_name.len + 1;
+ inarg = kmalloc(insize, GFP_KERNEL);
+ if(!inarg)
+ return -ENOMEM;
+
+ inarg->mode = mode;
+ inarg->rdev = rdev;
+ strcpy(inarg->name, entry->d_name.name);
+
+ in.h.opcode = FUSE_MKNOD;
+ in.h.ino = dir->i_ino;
+ in.argsize = insize;
+ in.arg = inarg;
+ out.argsize = sizeof(outarg);
+ out.arg = &outarg;
+ request_send(fc, &in, &out);
+ kfree(inarg);
+ if(out.h.result)
+ return out.h.result;
+
+ inode = iget(dir->i_sb, outarg.ino);
+ if(!inode)
+ return -ENOMEM;
+
+ init_inode(inode, &outarg.attr);
+ d_add(entry, inode);
+ return 0;
+}
+
+static int fuse_create(struct inode *dir, struct dentry *entry, int mode)
+{
+ return fuse_mknod(dir, entry, mode, 0);
+}
+
+static int fuse_permission(struct inode *inode, int mask)
+{
+
+ return 0;
+}
+
+static int fuse_revalidate(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
+ struct fuse_in in = FUSE_IN_INIT;
+ struct fuse_out out = FUSE_OUT_INIT;
+ struct fuse_getattr_out arg;
+
+ in.h.opcode = FUSE_GETATTR;
+ in.h.ino = inode->i_ino;
+ out.argsize = sizeof(arg);
+ out.arg = &arg;
+ request_send(fc, &in, &out);
+
+ if(out.h.result == 0)
+ change_attributes(inode, &arg.attr);
+
+ return out.h.result;
+}
+
+
+#define DIR_BUFSIZE 2048
+
+static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
+{
+ struct file *cfile = file->private_data;
+ char *buf;
+ char *p;
+ int ret;
+ size_t nbytes;
+
+ buf = kmalloc(DIR_BUFSIZE, GFP_KERNEL);
+ if(!buf)
+ return -ENOMEM;
+
+ ret = kernel_read(cfile, file->f_pos, buf, DIR_BUFSIZE);
+ if(ret < 0) {
+ printk("fuse_readdir: failed to read container file\n");
+ goto out;
+ }
+ nbytes = ret;
+ p = buf;
+ ret = 0;
+ while(nbytes >= FUSE_NAME_OFFSET) {
+ struct fuse_dirent *dirent = (struct fuse_dirent *) p;
+ size_t reclen = FUSE_DIRENT_SIZE(dirent);
+ int over;
+ if(dirent->namelen > NAME_MAX) {
+ printk("fuse_readdir: name too long\n");
+ ret = -EPROTO;
+ goto out;
+ }
+ if(reclen > nbytes)
+ break;
+
+ over = filldir(dstbuf, dirent->name, dirent->namelen,
+ file->f_pos, dirent->ino, dirent->type);
+ if(over)
+ break;
+
+ p += reclen;
+ file->f_pos += reclen;
+ nbytes -= reclen;
+ }
+
+ out:
+ kfree(buf);
+ return ret;
+}
+
+
+
+static int read_link(struct dentry *dentry, char **bufp)
+{
+ struct inode *inode = dentry->d_inode;
+ struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
+ struct fuse_in in = FUSE_IN_INIT;
+ struct fuse_out out = FUSE_OUT_INIT;
+ unsigned long page;
+
+ page = __get_free_page(GFP_KERNEL);
+ if(!page)
+ return -ENOMEM;
+
+ in.h.opcode = FUSE_READLINK;
+ in.h.ino = inode->i_ino;
+ out.arg = (void *) page;
+ out.argsize = PAGE_SIZE - 1;
+ out.argvar = 1;
+ request_send(fc, &in, &out);
+ if(out.h.result) {
+ free_page(page);
+ return out.h.result;
+ }
+
+ *bufp = (char *) page;
+ (*bufp)[out.argsize] = '\0';
+ return 0;
+}
+
+static void free_link(char *link)
+{
+ free_page((unsigned long) link);
+}
+
+static int fuse_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ int ret;
+ char *link;
+
+ ret = read_link(dentry, &link);
+ if(ret)
+ return ret;
+
+ ret = vfs_readlink(dentry, buffer, buflen, link);
+ free_link(link);
+ return ret;
+}
+
+static int fuse_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ int ret;
+ char *link;
+
+ ret = read_link(dentry, &link);
+ if(ret)
+ return ret;
+
+ ret = vfs_follow_link(nd, link);
+ free_link(link);
+ return ret;
+}
+
+static int fuse_dir_open(struct inode *inode, struct file *file)
+{
+ struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
+ struct fuse_in in = FUSE_IN_INIT;
+ struct fuse_out out = FUSE_OUT_INIT;
+ struct fuse_getdir_out outarg;
+
+ if(!(file->f_flags & O_DIRECTORY))
+ return -EISDIR;
+
+ in.h.opcode = FUSE_GETDIR;
+ in.h.ino = inode->i_ino;
+ out.argsize = sizeof(outarg);
+ out.arg = &outarg;
+ request_send(fc, &in, &out);
+ if(out.h.result == 0) {
+ struct file *cfile = outarg.file;
+ struct inode *inode;
+ if(!cfile) {
+ printk("fuse_getdir: invalid file\n");
+ return -EPROTO;
+ }
+ inode = cfile->f_dentry->d_inode;
+ if(!S_ISREG(inode->i_mode)) {
+ printk("fuse_getdir: not a regular file\n");
+ fput(cfile);
+ return -EPROTO;
+ }
+
+ file->private_data = cfile;
+ }
+
+ return out.h.result;
+}
+
+static int fuse_dir_release(struct inode *inode, struct file *file)
+{
+ struct file *cfile = file->private_data;
+
+ if(!cfile)
+ BUG();
+
+ fput(cfile);
+
+ return 0;
+}
+
+static struct inode_operations fuse_dir_inode_operations =
+{
+ lookup: fuse_lookup,
+ create: fuse_create,
+ mknod: fuse_mknod,
+#if 0
+
+ link: fuse_link,
+ unlink: fuse_unlink,
+ symlink: fuse_symlink,
+ mkdir: fuse_mkdir,
+ rmdir: fuse_rmdir,
+ rename: fuse_rename,
+#endif
+ permission: fuse_permission,
+ revalidate: fuse_revalidate,
+};
+
+static struct file_operations fuse_dir_operations = {
+ read: generic_read_dir,
+ readdir: fuse_readdir,
+ open: fuse_dir_open,
+ release: fuse_dir_release,
+};
+
+static struct inode_operations fuse_file_inode_operations = {
+ permission: fuse_permission,
+ revalidate: fuse_revalidate,
+};
+
+static struct inode_operations fuse_special_inode_operations = {
+ permission: fuse_permission,
+ revalidate: fuse_revalidate,
+};
+
+static struct file_operations fuse_file_operations = {
+};
+
+static struct inode_operations fuse_symlink_inode_operations =
+{
+ readlink: fuse_readlink,
+ follow_link: fuse_follow_link,
+ revalidate: fuse_revalidate,
+};
+
+void fuse_dir_init(struct inode *inode)
+{
+ inode->i_op = &fuse_dir_inode_operations;
+ inode->i_fop = &fuse_dir_operations;
+}
+
+void fuse_file_init(struct inode *inode)
+{
+ inode->i_op = &fuse_file_inode_operations;
+ inode->i_fop = &fuse_file_operations;
+}
+
+void fuse_symlink_init(struct inode *inode)
+{
+ inode->i_op = &fuse_symlink_inode_operations;
+}
+
+void fuse_special_init(struct inode *inode)
+{
+ inode->i_op = &fuse_special_inode_operations;
+}
+
+/*
+ * Local Variables:
+ * indent-tabs-mode: t
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
new file mode 100644
index 0000000..1eb0bb4
--- /dev/null
+++ b/kernel/fuse_i.h
@@ -0,0 +1,169 @@
+/*
+ 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 <linux/fuse.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+
+#define FUSE_VERSION "0.1"
+
+#define MAX_CLEARED 256
+
+/**
+ * A Fuse connection.
+ *
+ * This structure is created, when the client device is opened, and is
+ * destroyed, when the client device is closed _and_ the filesystem is
+ * umounted.
+ */
+struct fuse_conn {
+ /** The superblock of the mounted filesystem */
+ struct super_block *sb;
+
+ /** The opened client device */
+ struct file *file;
+
+ /** The client wait queue */
+ wait_queue_head_t waitq;
+
+ /** The list of pending requests */
+ struct list_head pending;
+
+ /** The list of requests being processed */
+ struct list_head processing;
+
+ /** The number of cleared inodes */
+ unsigned int numcleared;
+
+ /** The array of cleared inode numbers */
+ unsigned long *cleared;
+
+ /** Connnection number (for debuging) */
+ int id;
+
+ /** The request id */
+ int reqctr;
+};
+
+/**
+ * A request to the client
+ */
+struct fuse_req {
+ /** The request list */
+ struct list_head list;
+
+ /** The request ID */
+ int unique;
+
+ /** The opcode */
+ enum fuse_opcode opcode;
+
+ /** The request input size */
+ unsigned int insize;
+
+ /** The request input */
+ char *in;
+
+ /** The maximum request output size, if zero, then the request is
+ asynchronous */
+ unsigned int outsize;
+
+ /** The request output */
+ char *out;
+
+ /** The request wait queue */
+ wait_queue_head_t waitq;
+};
+
+
+struct fuse_in {
+ struct fuse_in_header h;
+ unsigned int argsize;
+ const void *arg;
+};
+
+struct fuse_out {
+ struct fuse_out_header h;
+ unsigned int argsize;
+ unsigned int argvar;
+ void *arg;
+};
+
+#define FUSE_IN_INIT { {0, 0, 0}, 0, 0 }
+#define FUSE_OUT_INIT { {0, 0}, 0, 0, 0 }
+
+
+/**
+ * The proc entry for the client device ("/proc/fs/fuse/dev")
+ */
+extern struct proc_dir_entry *proc_fuse_dev;
+
+/**
+ * The lock to protect fuses structures
+ */
+extern spinlock_t fuse_lock;
+
+/**
+ * Fill in the directory operations
+ */
+void fuse_dir_init(struct inode *inode);
+
+/**
+ * Fill in the file operations
+ */
+void fuse_file_init(struct inode *inode);
+
+/**
+ * Fill in the symlink operations
+ */
+void fuse_symlink_init(struct inode *inode);
+
+/**
+ * Fill in the special inode operaions
+ */
+void fuse_special_init(struct inode *inode);
+
+/**
+ * Check if the connection can be released, and if yes, then free the
+ * connection structure
+ */
+void fuse_release_conn(struct fuse_conn *fc);
+
+/**
+ * Initialize the client device
+ */
+int fuse_dev_init(void);
+
+/**
+ * Cleanup the client device
+ */
+void fuse_dev_cleanup(void);
+
+/**
+ * Initialize the fuse filesystem
+ */
+int fuse_fs_init(void);
+
+/**
+ * Cleanup the fuse filesystem
+ */
+void fuse_fs_cleanup(void);
+
+/**
+ * Send a request
+ *
+ */
+void request_send(struct fuse_conn *fc, struct fuse_in *in,
+ struct fuse_out *out);
+
+/*
+ * Local Variables:
+ * indent-tabs-mode: t
+ * c-basic-offset: 8
+ */
diff --git a/kernel/inode.c b/kernel/inode.c
new file mode 100644
index 0000000..ac2d6be
--- /dev/null
+++ b/kernel/inode.c
@@ -0,0 +1,228 @@
+/*
+ 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/sched.h>
+#include <linux/slab.h>
+#include <linux/file.h>
+
+#define FUSE_SUPER_MAGIC 0x65735546
+
+static void fuse_read_inode(struct inode *inode)
+{
+ /* No op */
+}
+
+static void send_forget(struct fuse_conn *fc, unsigned long *forget,
+ unsigned int numforget)
+{
+ struct fuse_in in = FUSE_IN_INIT;
+
+ in.h.opcode = FUSE_FORGET;
+ in.h.ino = 0;
+ in.argsize = numforget * sizeof(unsigned long);
+ in.arg = forget;
+
+ request_send(fc, &in, NULL);
+}
+
+static int alloc_cleared(struct fuse_conn *fc)
+{
+ unsigned long *tmp;
+
+ spin_unlock(&fuse_lock);
+ tmp = kmalloc(sizeof(unsigned long) * MAX_CLEARED, GFP_KERNEL);
+ spin_lock(&fuse_lock);
+
+ if(!fc->file || fc->cleared != NULL)
+ kfree(tmp);
+ else if(!tmp)
+ printk("fuse_clear_inode: Cannot allocate memory\n");
+ else
+ fc->cleared = tmp;
+
+ return fc->cleared != NULL;
+}
+
+static unsigned long *add_cleared(struct fuse_conn *fc, unsigned long ino)
+{
+ if(!fc->file || (!fc->cleared && !alloc_cleared(fc)))
+ return NULL;
+
+ fc->cleared[fc->numcleared] = ino;
+ fc->numcleared ++;
+
+ if(fc->numcleared == MAX_CLEARED) {
+ unsigned long *tmp = fc->cleared;
+ fc->cleared = NULL;
+ fc->numcleared = 0;
+ return tmp;
+ }
+
+ return NULL;
+}
+
+static void fuse_clear_inode(struct inode *inode)
+{
+ struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
+ unsigned long *forget;
+
+ spin_lock(&fuse_lock);
+ forget = add_cleared(fc, inode->i_ino);
+ spin_unlock(&fuse_lock);
+
+ if(forget) {
+ send_forget(fc, forget, MAX_CLEARED);
+ kfree(forget);
+ }
+}
+
+static void fuse_put_super(struct super_block *sb)
+{
+ struct fuse_conn *fc = sb->u.generic_sbp;
+
+ spin_lock(&fuse_lock);
+ fc->sb = NULL;
+ fuse_release_conn(fc);
+ spin_unlock(&fuse_lock);
+
+}
+
+static struct super_operations fuse_super_operations = {
+ read_inode: fuse_read_inode,
+ clear_inode: fuse_clear_inode,
+ put_super: fuse_put_super,
+};
+
+
+static struct fuse_conn *get_conn(struct fuse_mount_data *d)
+{
+ struct fuse_conn *fc = NULL;
+ struct file *file;
+ struct inode *ino;
+
+ if(d == NULL) {
+ printk("fuse_read_super: Bad mount data\n");
+ return NULL;
+ }
+
+ if(d->version != FUSE_MOUNT_VERSION) {
+ printk("fuse_read_super: Bad mount version: %i\n", d->version);
+ return NULL;
+ }
+
+ file = fget(d->fd);
+ ino = NULL;
+ if(file)
+ ino = file->f_dentry->d_inode;
+
+ if(!ino || ino->u.generic_ip != proc_fuse_dev) {
+ printk("fuse_read_super: Bad file: %i\n", d->fd);
+ goto out;
+ }
+
+ fc = file->private_data;
+
+ out:
+ fput(file);
+ return fc;
+
+}
+
+static struct inode *get_root_inode(struct super_block *sb)
+{
+ struct inode *root ;
+
+ root = iget(sb, 1);
+ if(root) {
+ root->i_mode = S_IFDIR;
+ root->i_uid = 0;
+ root->i_gid = 0;
+ root->i_nlink = 2;
+ root->i_size = 0;
+ root->i_blksize = 1024;
+ root->i_blocks = 0;
+ root->i_atime = CURRENT_TIME;
+ root->i_mtime = CURRENT_TIME;
+ root->i_ctime = CURRENT_TIME;
+ fuse_dir_init(root);
+ }
+
+ return root;
+}
+
+static struct super_block *fuse_read_super(struct super_block *sb,
+ void *data, int silent)
+{
+ struct fuse_conn *fc;
+ struct inode *root;
+
+ sb->s_blocksize = 1024;
+ sb->s_blocksize_bits = 10;
+ sb->s_magic = FUSE_SUPER_MAGIC;
+ sb->s_op = &fuse_super_operations;
+
+ root = get_root_inode(sb);
+ if(root == NULL) {
+ printk("fuse_read_super: failed to get root inode\n");
+ return NULL;
+ }
+
+ spin_lock(&fuse_lock);
+ fc = get_conn(data);
+ if(fc == NULL)
+ goto err;
+
+ if(fc->sb != NULL) {
+ printk("fuse_read_super: connection %i already mounted\n",
+ fc->id);
+ goto err;
+ }
+
+ sb->u.generic_sbp = fc;
+ sb->s_root = d_alloc_root(root);
+ fc->sb = sb;
+ spin_unlock(&fuse_lock);
+
+ return sb;
+
+ err:
+ spin_unlock(&fuse_lock);
+ iput(root);
+ return NULL;
+}
+
+
+static DECLARE_FSTYPE(fuse_fs_type, "fuse", fuse_read_super, 0);
+
+int fuse_fs_init()
+{
+ int res;
+
+ res = register_filesystem(&fuse_fs_type);
+ if(res)
+ printk("fuse: failed to register filesystem\n");
+
+ return res;
+}
+
+void fuse_fs_cleanup()
+{
+ unregister_filesystem(&fuse_fs_type);
+}
+
+/*
+ * Local Variables:
+ * indent-tabs-mode: t
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/kernel/util.c b/kernel/util.c
new file mode 100644
index 0000000..57b1a90
--- /dev/null
+++ b/kernel/util.c
@@ -0,0 +1,61 @@
+/*
+ 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/slab.h>
+
+#define FUSE_VERSION "0.1"
+
+spinlock_t fuse_lock = SPIN_LOCK_UNLOCKED;
+
+/* Must be called with the fuse lock held */
+void fuse_release_conn(struct fuse_conn *fc)
+{
+ if(fc->sb == NULL && fc->file == NULL) {
+ kfree(fc);
+ }
+}
+
+int init_module(void)
+{
+ int res;
+
+ printk(KERN_DEBUG "fuse init (version %s)\n", FUSE_VERSION);
+
+ res = fuse_fs_init();
+ if(res)
+ goto err;
+
+ res = fuse_dev_init();
+ if(res)
+ goto err_fs_cleanup;
+
+ return 0;
+
+ err_fs_cleanup:
+ fuse_fs_cleanup();
+ err:
+ return res;
+}
+
+void cleanup_module(void)
+{
+ printk(KERN_DEBUG "fuse cleanup\n");
+
+ fuse_fs_cleanup();
+ fuse_dev_cleanup();
+}
+
+/*
+ * Local Variables:
+ * indent-tabs-mode: t
+ * c-basic-offset: 8
+ */