diff options
author | 2001-10-28 19:44:14 +0000 | |
---|---|---|
committer | 2001-10-28 19:44:14 +0000 | |
commit | 85c74fcdfd9e67d411c3e1734b34effd0d73fa4d (patch) | |
tree | 908e39d3e0b84bd733261cdde16ef6ae707f2352 /kernel | |
parent | 90d8bef61c8c40472ddfb1aafeeb6473ec51a053 (diff) |
x
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/Makefile | 16 | ||||
-rw-r--r-- | kernel/dev.c | 440 | ||||
-rw-r--r-- | kernel/dir.c | 384 | ||||
-rw-r--r-- | kernel/fuse_i.h | 169 | ||||
-rw-r--r-- | kernel/inode.c | 228 | ||||
-rw-r--r-- | kernel/util.c | 61 |
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 + */ |