lkml.org 
[lkml]   [2004]   [Nov]   [20]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Subject[PATCH 3/13] Filesystem in Userspace
    From
    Date
    This patch adds FUSE core. 

    This contains the following files:

    o dev.c
    - fuse device operations (read, write, release, poll)
    - adds /sys/fs/fuse/version
    - registers misc device

    o dir.c
    - directory operations (only lookup this patch)

    o inode.c
    - superblock operations (alloc_inode, destroy_inode, read_inode,
    clear_inode, put_super, show_options)
    - registers FUSE filesystem

    o util.c
    - module initialization and cleanup

    o fuse_i.h
    - private header file

    Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
    --- linux-2.6.10-rc2/fs/fuse/dev.c 1970-01-01 01:00:00.000000000 +0100
    +++ linux-2.6.10-rc2-fuse/fs/fuse/dev.c 2004-11-20 22:56:25.000000000 +0100
    @@ -0,0 +1,538 @@
    +/*
    + FUSE: Filesystem in Userspace
    + Copyright (C) 2001-2004 Miklos Szeredi <miklos@szeredi.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/poll.h>
    +#include <linux/kobject.h>
    +#include <linux/miscdevice.h>
    +#include <linux/file.h>
    +
    +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 = 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 = kmem_cache_alloc(fuse_req_cachep, SLAB_KERNEL);
    + if (req) {
    + memset(req, 0, sizeof(*req));
    + INIT_LIST_HEAD(&req->list);
    + init_waitqueue_head(&req->waitq);
    + }
    + return req;
    +}
    +
    +void fuse_request_free(struct fuse_req *req)
    +{
    + kmem_cache_free(fuse_req_cachep, req);
    +}
    +
    +/* Called with fuse_lock held. Releases, and then reaquires it. */
    +static void request_wait_answer(struct fuse_req *req)
    +{
    + spin_unlock(&fuse_lock);
    + wait_event(req->waitq, req->finished);
    + spin_lock(&fuse_lock);
    +}
    +
    +static int get_unique(struct fuse_conn *fc)
    +{
    + fc->reqctr++;
    + if (fc->reqctr == 0)
    + fc->reqctr = 1;
    + return fc->reqctr;
    +}
    +
    +void fuse_reset_request(struct fuse_req *req)
    +{
    + int preallocated = req->preallocated;
    +
    + memset(req, 0, sizeof(*req));
    + INIT_LIST_HEAD(&req->list);
    + init_waitqueue_head(&req->waitq);
    + req->preallocated = preallocated;
    +}
    +
    +static struct fuse_req *do_get_request(struct fuse_conn *fc)
    +{
    + struct fuse_req *req;
    +
    + spin_lock(&fuse_lock);
    + BUG_ON(list_empty(&fc->unused_list));
    + req = list_entry(fc->unused_list.next, struct fuse_req, list);
    + list_del_init(&req->list);
    + spin_unlock(&fuse_lock);
    + fuse_reset_request(req);
    + req->in.h.uid = current->fsuid;
    + req->in.h.gid = current->fsgid;
    + req->in.h.pid = current->pid;
    + return req;
    +}
    +
    +struct fuse_req *fuse_get_request(struct fuse_conn *fc)
    +{
    + if (down_interruptible(&fc->unused_sem))
    + return NULL;
    + return do_get_request(fc);
    +}
    +
    +struct fuse_req *fuse_get_request_nonblock(struct fuse_conn *fc)
    +{
    + if (down_trylock(&fc->unused_sem))
    + return NULL;
    + return do_get_request(fc);
    +}
    +
    +void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
    +{
    + if (!req->preallocated)
    + fuse_request_free(req);
    + else {
    + spin_lock(&fuse_lock);
    + list_add(&req->list, &fc->unused_list);
    + spin_unlock(&fuse_lock);
    + up(&fc->unused_sem);
    + }
    +}
    +
    +/* Must be called with fuse_lock held, and unlocks it */
    +static void request_end(struct fuse_conn *fc, struct fuse_req *req)
    +{
    + wake_up(&req->waitq);
    + spin_unlock(&fuse_lock);
    +}
    +
    +void request_send(struct fuse_conn *fc, struct fuse_req *req)
    +{
    + 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);
    + request_wait_answer(req);
    + list_del(&req->list);
    + }
    + spin_unlock(&fuse_lock);
    +}
    +
    +void request_send_noreply(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);
    + spin_unlock(&fuse_lock);
    + } else {
    + spin_unlock(&fuse_lock);
    + fuse_put_request(fc, req);
    + }
    +}
    +
    +static void request_wait(struct fuse_conn *fc)
    +{
    + DECLARE_WAITQUEUE(wait, current);
    +
    + add_wait_queue_exclusive(&fc->waitq, &wait);
    + while (fc->sb && list_empty(&fc->pending)) {
    + set_current_state(TASK_INTERRUPTIBLE);
    + 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);
    +}
    +
    +static inline int copy_in_one(const void *src, size_t srclen,
    + char __user **dstp, size_t *dstlenp)
    +{
    + if (*dstlenp < srclen) {
    + printk("fuse_dev_read: buffer too small\n");
    + return -EINVAL;
    + }
    +
    + if (srclen && copy_to_user(*dstp, src, srclen))
    + return -EFAULT;
    +
    + *dstp += srclen;
    + *dstlenp -= srclen;
    +
    + return 0;
    +}
    +
    +static inline int copy_in_args(struct fuse_in *in, char __user *buf,
    + size_t nbytes)
    +{
    + int err;
    + int i;
    + size_t orignbytes = nbytes;
    +
    + err = copy_in_one(&in->h, sizeof(in->h), &buf, &nbytes);
    + if (err)
    + return err;
    +
    + for (i = 0; i < in->numargs; i++) {
    + struct fuse_in_arg *arg = &in->args[i];
    + err = copy_in_one(arg->value, arg->size, &buf, &nbytes);
    + if (err)
    + return err;
    + }
    +
    + return orignbytes - nbytes;
    +}
    +
    +static ssize_t fuse_dev_read(struct file *file, char __user *buf,
    + size_t nbytes, loff_t *off)
    +{
    + ssize_t ret;
    + struct fuse_conn *fc;
    + struct fuse_req *req = NULL;
    +
    + spin_lock(&fuse_lock);
    + fc = file->private_data;
    + if (!fc) {
    + spin_unlock(&fuse_lock);
    + return -EPERM;
    + }
    + request_wait(fc);
    + 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);
    + }
    + spin_unlock(&fuse_lock);
    + if (!fc)
    + return -ENODEV;
    + if (req == NULL)
    + return -EINTR;
    +
    + ret = copy_in_args(&req->in, buf, nbytes);
    + spin_lock(&fuse_lock);
    + if (req->isreply) {
    + if (ret < 0) {
    + req->out.h.error = -EPROTO;
    + req->finished = 1;
    + } else
    + list_add_tail(&req->list, &fc->processing);
    + if (ret < 0)
    + /* Unlocks fuse_lock: */
    + request_end(fc, req);
    + else
    + spin_unlock(&fuse_lock);
    + } else {
    + spin_unlock(&fuse_lock);
    + fuse_put_request(fc, req);
    + }
    + 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->in.h.unique == unique) {
    + req = tmp;
    + break;
    + }
    + }
    +
    + return req;
    +}
    +
    +static inline int copy_out_one(struct fuse_out_arg *arg,
    + const char __user **srcp,
    + size_t *srclenp, int allowvar)
    +{
    + size_t dstlen = arg->size;
    + if (*srclenp < dstlen) {
    + if (!allowvar) {
    + printk("fuse_dev_write: write is short\n");
    + return -EINVAL;
    + }
    + dstlen = *srclenp;
    + }
    +
    + if (dstlen && copy_from_user(arg->value, *srcp, dstlen))
    + return -EFAULT;
    +
    + *srcp += dstlen;
    + *srclenp -= dstlen;
    + arg->size = dstlen;
    +
    + return 0;
    +}
    +
    +static inline int copy_out_args(struct fuse_req *req, const char __user *buf,
    + size_t nbytes)
    +{
    + struct fuse_out *out = &req->out;
    + int err;
    + int i;
    +
    + buf += sizeof(struct fuse_out_header);
    + nbytes -= sizeof(struct fuse_out_header);
    +
    + if (!out->h.error) {
    + for (i = 0; i < out->numargs; i++) {
    + struct fuse_out_arg *arg = &out->args[i];
    + int allowvar;
    +
    + if (out->argvar && i == out->numargs - 1)
    + allowvar = 1;
    + else
    + allowvar = 0;
    +
    + err = copy_out_one(arg, &buf, &nbytes, allowvar);
    + if (err)
    + return err;
    + }
    + }
    +
    + if (nbytes != 0) {
    + printk("fuse_dev_write: write is long\n");
    + return -EINVAL;
    + }
    +
    + return 0;
    +}
    +
    +static inline int copy_out_header(struct fuse_out_header *oh,
    + const char __user *buf, size_t nbytes)
    +{
    + if (nbytes < sizeof(struct fuse_out_header)) {
    + printk("fuse_dev_write: write is short\n");
    + return -EINVAL;
    + }
    +
    + if (copy_from_user(oh, buf, sizeof(struct fuse_out_header)))
    + return -EFAULT;
    +
    + return 0;
    +}
    +
    +static ssize_t fuse_dev_write(struct file *file, const char __user *buf,
    + size_t nbytes, loff_t *off)
    +{
    + int err;
    + struct fuse_conn *fc = fuse_get_conn(file);
    + struct fuse_req *req;
    + struct fuse_out_header oh;
    +
    + if (!fc)
    + return -ENODEV;
    +
    + err = copy_out_header(&oh, buf, nbytes);
    + if (err)
    + return err;
    +
    + if (!oh.unique) {
    + err = -EINVAL;
    + goto out;
    + }
    +
    + if (oh.error <= -1000 || oh.error > 0) {
    + printk("fuse_dev_write: bad error value\n");
    + return -EINVAL;
    + }
    +
    + spin_lock(&fuse_lock);
    + req = request_find(fc, oh.unique);
    + if (req != NULL)
    + list_del_init(&req->list);
    + spin_unlock(&fuse_lock);
    + if (!req)
    + return -ENOENT;
    +
    + req->out.h = oh;
    + err = copy_out_args(req, buf, nbytes);
    +
    + spin_lock(&fuse_lock);
    + if (err)
    + req->out.h.error = -EPROTO;
    +
    + req->finished = 1;
    + /* Unlocks fuse_lock: */
    + request_end(fc, req);
    +
    + out:
    + if (!err)
    + return nbytes;
    + else
    + return err;
    +}
    +
    +static unsigned int fuse_dev_poll(struct file *file, poll_table *wait)
    +{
    + struct fuse_conn *fc = fuse_get_conn(file);
    + unsigned int mask = POLLOUT | POLLWRNORM;
    +
    + if (!fc)
    + return -ENODEV;
    +
    + 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 void end_requests(struct fuse_conn *fc, 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->isreply) {
    + req->out.h.error = -ECONNABORTED;
    + req->finished = 1;
    + /* Unlocks fuse_lock: */
    + request_end(fc, req);
    + spin_lock(&fuse_lock);
    + } else {
    + spin_unlock(&fuse_lock);
    + fuse_put_request(fc, req);
    + spin_lock(&fuse_lock);
    + }
    + }
    +}
    +
    +static int fuse_dev_release(struct inode *inode, struct file *file)
    +{
    + struct fuse_conn *fc;
    +
    + spin_lock(&fuse_lock);
    + fc = 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;
    +}
    +
    +struct file_operations fuse_dev_operations = {
    + .owner = THIS_MODULE,
    + .read = fuse_dev_read,
    + .write = fuse_dev_write,
    + .poll = fuse_dev_poll,
    + .release = fuse_dev_release,
    +};
    +
    +#define FUSE_MINOR MISC_DYNAMIC_MINOR
    +
    +static decl_subsys(fuse, NULL, NULL);
    +
    +static ssize_t version_show(struct subsystem *subsys, char *buf)
    +{
    + return sprintf(buf, "%i.%i\n", FUSE_KERNEL_VERSION,
    + FUSE_KERNEL_MINOR_VERSION);
    +}
    +static struct subsys_attribute fuse_attr_version = __ATTR_RO(version);
    +
    +static struct miscdevice fuse_miscdevice = {
    + .minor = FUSE_MINOR,
    + .name = "fuse",
    + .fops = &fuse_dev_operations,
    +};
    +
    +static int __init fuse_sysfs_init(void)
    +{
    + int err;
    + err = fs_subsys_register(&fuse_subsys);
    + if (err)
    + return err;
    + err = subsys_create_file(&fuse_subsys, &fuse_attr_version);
    + if (err) {
    + subsystem_unregister(&fuse_subsys);
    + return err;
    + }
    + return 0;
    +}
    +
    +static void fuse_sysfs_clean(void)
    +{
    + subsys_remove_file(&fuse_subsys, &fuse_attr_version);
    + subsystem_unregister(&fuse_subsys);
    +}
    +
    +static int __init fuse_device_init(void)
    +{
    + int err = fuse_sysfs_init();
    + if (err)
    + return err;
    +
    + err = misc_register(&fuse_miscdevice);
    + if (err) {
    + fuse_sysfs_clean();
    + return err;
    + }
    + return 0;
    +}
    +
    +static void fuse_device_clean(void)
    +{
    + misc_deregister(&fuse_miscdevice);
    + fuse_sysfs_clean();
    +}
    +
    +int __init fuse_dev_init(void)
    +{
    + int err;
    + err = fuse_device_init();
    + if (err)
    + goto out;
    +
    + err = -ENOMEM;
    + fuse_req_cachep = kmem_cache_create("fuser_request",
    + sizeof(struct fuse_req),
    + 0, 0, NULL, NULL);
    + if (!fuse_req_cachep)
    + goto out_device_clean;
    +
    + return 0;
    +
    + out_device_clean:
    + fuse_device_clean();
    + out:
    + return err;
    +}
    +
    +void fuse_dev_cleanup(void)
    +{
    + fuse_device_clean();
    + kmem_cache_destroy(fuse_req_cachep);
    +}
    --- linux-2.6.10-rc2/fs/fuse/dir.c 1970-01-01 01:00:00.000000000 +0100
    +++ linux-2.6.10-rc2-fuse/fs/fuse/dir.c 2004-11-20 22:56:25.000000000 +0100
    @@ -0,0 +1,166 @@
    +/*
    + FUSE: Filesystem in Userspace
    + Copyright (C) 2001-2004 Miklos Szeredi <miklos@szeredi.hu>
    +
    + This program can be distributed under the terms of the GNU GPL.
    + See the file COPYING.
    +*/
    +
    +#include "fuse_i.h"
    +
    +#include <linux/pagemap.h>
    +#include <linux/file.h>
    +
    +static struct inode_operations fuse_dir_inode_operations;
    +
    +static void change_attributes(struct inode *inode, struct fuse_attr *attr)
    +{
    + if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size) {
    + invalidate_inode_pages(inode->i_mapping);
    + }
    +
    + 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;
    + 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;
    +}
    +
    +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_ISDIR(inode->i_mode))
    + inode->i_op = &fuse_dir_inode_operations;
    +}
    +
    +static int fuse_inode_eq(struct inode *inode, void *_nodeidp)
    +{
    + unsigned long nodeid = *(unsigned long *) _nodeidp;
    + struct fuse_inode *fi = INO_FI(inode);
    + if (fi->nodeid == nodeid)
    + return 1;
    + else
    + return 0;
    +}
    +
    +static int fuse_inode_set(struct inode *inode, void *_nodeidp)
    +{
    + unsigned long nodeid = *(unsigned long *) _nodeidp;
    + struct fuse_inode *fi = INO_FI(inode);
    + fi->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;
    +
    + 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;
    + fuse_init_inode(inode, attr);
    + unlock_new_inode(inode);
    + } else if (inode->i_generation != generation)
    + printk("fuse_iget: bad generation for node %lu\n", nodeid);
    +
    + change_attributes(inode, attr);
    + inode->i_version = version;
    + return inode;
    +}
    +
    +static int fuse_send_lookup(struct fuse_conn *fc, struct fuse_req *req,
    + struct inode *dir, struct dentry *entry,
    + struct fuse_entry_out *outarg, int *version)
    +{
    + struct fuse_inode *fi = INO_FI(dir);
    + req->in.h.opcode = FUSE_LOOKUP;
    + req->in.h.nodeid = fi->nodeid;
    + req->in.numargs = 1;
    + req->in.args[0].size = entry->d_name.len + 1;
    + req->in.args[0].value = entry->d_name.name;
    + req->out.numargs = 1;
    + req->out.args[0].size = sizeof(struct fuse_entry_out);
    + req->out.args[0].value = outarg;
    + request_send(fc, req);
    + *version = req->out.h.unique;
    + return req->out.h.error;
    +}
    +
    +static inline unsigned long time_to_jiffies(unsigned long sec,
    + unsigned long nsec)
    +{
    + /* prevent wrapping of jiffies */
    + if (sec + 1 >= LONG_MAX / HZ)
    + return 0;
    +
    + return jiffies + sec * HZ + nsec / (1000000000 / HZ);
    +}
    +
    +static int fuse_lookup_iget(struct inode *dir, struct dentry *entry,
    + struct inode **inodep)
    +{
    + struct fuse_conn *fc = INO_FC(dir);
    + int err;
    + struct fuse_entry_out outarg;
    + int version;
    + struct inode *inode = NULL;
    + struct fuse_req *req;
    +
    + if (entry->d_name.len > FUSE_NAME_MAX)
    + return -ENAMETOOLONG;
    + req = fuse_get_request(fc);
    + if (!req)
    + return -ERESTARTSYS;
    +
    + err = fuse_send_lookup(fc, req, dir, entry, &outarg, &version);
    + if (!err) {
    + inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
    + &outarg.attr, version);
    + if (!inode) {
    + fuse_send_forget(fc, req, outarg.nodeid, version);
    + return -ENOMEM;
    + }
    + }
    + fuse_put_request(fc, req);
    + if (err && err != -ENOENT)
    + return err;
    +
    + if (inode) {
    + struct fuse_inode *fi = INO_FI(inode);
    + 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);
    + }
    +
    + *inodep = inode;
    + return 0;
    +}
    +
    +static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
    + struct nameidata *nd)
    +{
    + struct inode *inode;
    + int err = fuse_lookup_iget(dir, entry, &inode);
    + if (err)
    + return ERR_PTR(err);
    + return d_splice_alias(inode, entry);
    +}
    +
    +static struct inode_operations fuse_dir_inode_operations = {
    + .lookup = fuse_lookup,
    +};
    --- linux-2.6.10-rc2/fs/fuse/fuse_i.h 1970-01-01 01:00:00.000000000 +0100
    +++ linux-2.6.10-rc2-fuse/fs/fuse/fuse_i.h 2004-11-20 22:56:25.000000000 +0100
    @@ -0,0 +1,216 @@
    +/*
    + FUSE: Filesystem in Userspace
    + Copyright (C) 2001-2004 Miklos Szeredi <miklos@szeredi.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/wait.h>
    +#include <linux/list.h>
    +#include <linux/spinlock.h>
    +#include <asm/semaphore.h>
    +
    +/* If more requests are outstanding, then the operation will block */
    +#define FUSE_MAX_OUTSTANDING 10
    +
    +/** FUSE specific inode data */
    +struct fuse_inode {
    + unsigned long nodeid;
    + struct fuse_req *forget_req;
    + unsigned long i_time;
    +};
    +
    +/** One input argument of a request */
    +struct fuse_in_arg {
    + unsigned int size;
    + const void *value;
    +};
    +
    +/** The request input */
    +struct fuse_in {
    + struct fuse_in_header h;
    + unsigned int numargs;
    + struct fuse_in_arg args[3];
    +};
    +
    +/** One output argument of a request */
    +struct fuse_out_arg {
    + unsigned int size;
    + void *value;
    +};
    +
    +/** The request output */
    +struct fuse_out {
    + struct fuse_out_header h;
    + unsigned int argvar;
    + unsigned int numargs;
    + struct fuse_out_arg args[3];
    +};
    +
    +struct fuse_req;
    +struct fuse_conn;
    +
    +/**
    + * A request to the client
    + */
    +struct fuse_req {
    + /** The request list */
    + struct list_head list;
    +
    + /** True if the request has reply */
    + unsigned int isreply:1;
    +
    + /* The request is preallocated */
    + unsigned int preallocated:1;
    +
    + /* The request is finished */
    + unsigned int finished;
    +
    + /** The request input */
    + struct fuse_in in;
    +
    + /** The request output */
    + struct fuse_out out;
    +
    + /** Used to wake up the task waiting for completion of request*/
    + wait_queue_head_t waitq;
    +
    + /** Data for asynchronous requests */
    + union {
    + struct fuse_forget_in forget_in;
    + } misc;
    +};
    +
    +/**
    + * 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
    + * unmounted.
    + */
    +struct fuse_conn {
    + /** The superblock of the mounted filesystem */
    + struct super_block *sb;
    +
    + /** The opened client device */
    + struct file *file;
    +
    + /** The user id for this mount */
    + uid_t uid;
    +
    + /** Readers of the connection are waiting on this */
    + wait_queue_head_t waitq;
    +
    + /** The list of pending requests */
    + struct list_head pending;
    +
    + /** The list of requests being processed */
    + struct list_head processing;
    +
    + /** Controls the maximum number of outstanding requests */
    + struct semaphore unused_sem;
    +
    + /** The list of unused requests */
    + struct list_head unused_list;
    +
    + /** The next unique request id */
    + int reqctr;
    +};
    +
    +#define SB_FC(sb) ((sb)->s_fs_info)
    +#define INO_FC(inode) SB_FC((inode)->i_sb)
    +#define INO_FI(i) ((struct fuse_inode *) (((struct inode *)(i))+1))
    +
    +/** Device operations */
    +extern struct file_operations fuse_dev_operations;
    +
    +/**
    + * This is the single global spinlock which protects FUSE's structures
    + *
    + * The following data is protected by this lock:
    + *
    + * - the private_data field of the device file
    + * - the s_fs_info field of the super block
    + * - unused_list, pending, processing lists in fuse_conn
    + * - the unique request ID counter reqctr in fuse_conn
    + * - the sb (super_block) field in fuse_conn
    + * - the file (device file) field in fuse_conn
    + */
    +extern spinlock_t fuse_lock;
    +
    +/**
    + * Get a filled in inode
    + */
    +struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
    + int generation, struct fuse_attr *attr, int version);
    +
    +/**
    + * Send FORGET command
    + */
    +void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
    + unsigned long nodeid, int version);
    +
    +/**
    + * 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);
    +
    +/**
    + * Allocate a request
    + */
    +struct fuse_req *fuse_request_alloc(void);
    +
    +/**
    + * Free a request
    + */
    +void fuse_request_free(struct fuse_req *req);
    +
    +/**
    + * Reinitialize a request, the preallocated flag is left unmodified
    + */
    +void fuse_reset_request(struct fuse_req *req);
    +
    +/**
    + * Reserve a preallocated request
    + */
    +struct fuse_req *fuse_get_request(struct fuse_conn *fc);
    +
    +/**
    + * Free a request
    + */
    +void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req);
    +
    +/**
    + * Send a request
    + */
    +void request_send(struct fuse_conn *fc, struct fuse_req *req);
    +
    +/**
    + * Send a request for which a reply is not expected
    + */
    +void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req);
    --- linux-2.6.10-rc2/fs/fuse/inode.c 1970-01-01 01:00:00.000000000 +0100
    +++ linux-2.6.10-rc2-fuse/fs/fuse/inode.c 2004-11-20 22:56:25.000000000 +0100
    @@ -0,0 +1,361 @@
    +/*
    + FUSE: Filesystem in Userspace
    + Copyright (C) 2001-2004 Miklos Szeredi <miklos@szeredi.hu>
    +
    + This program can be distributed under the terms of the GNU GPL.
    + See the file COPYING.
    +*/
    +
    +#include "fuse_i.h"
    +
    +#include <linux/pagemap.h>
    +#include <linux/slab.h>
    +#include <linux/file.h>
    +#include <linux/mount.h>
    +#include <linux/seq_file.h>
    +#include <linux/module.h>
    +#include <linux/moduleparam.h>
    +#include <linux/parser.h>
    +
    +static kmem_cache_t *fuse_inode_cachep;
    +
    +#define FUSE_SUPER_MAGIC 0x65735546
    +
    +struct fuse_mount_data {
    + int fd;
    + unsigned int rootmode;
    + unsigned int uid;
    +};
    +
    +static struct inode *fuse_alloc_inode(struct super_block *sb)
    +{
    + struct inode *inode;
    + struct fuse_inode *fi;
    +
    + inode = kmem_cache_alloc(fuse_inode_cachep, SLAB_KERNEL);
    + if (!inode)
    + return NULL;
    +
    +
    + fi = INO_FI(inode);
    + memset(fi, 0, sizeof(*fi));
    + fi->forget_req = fuse_request_alloc();
    + if (!fi->forget_req) {
    + kmem_cache_free(fuse_inode_cachep, inode);
    + return NULL;
    + }
    + return inode;
    +}
    +
    +static void fuse_destroy_inode(struct inode *inode)
    +{
    + struct fuse_inode *fi = INO_FI(inode);
    + if (fi->forget_req)
    + fuse_request_free(fi->forget_req);
    + kmem_cache_free(fuse_inode_cachep, inode);
    +}
    +
    +static void fuse_read_inode(struct inode *inode)
    +{
    + /* No op */
    +}
    +
    +void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
    + unsigned long nodeid, int version)
    +{
    + struct fuse_forget_in *inarg = &req->misc.forget_in;
    + inarg->version = version;
    + req->in.h.opcode = FUSE_FORGET;
    + req->in.h.nodeid = nodeid;
    + req->in.numargs = 1;
    + req->in.args[0].size = sizeof(struct fuse_forget_in);
    + req->in.args[0].value = inarg;
    + request_send_noreply(fc, req);
    +}
    +
    +static void fuse_clear_inode(struct inode *inode)
    +{
    + struct fuse_conn *fc = INO_FC(inode);
    +
    + if (fc) {
    + struct fuse_inode *fi = INO_FI(inode);
    + fuse_send_forget(fc, fi->forget_req, fi->nodeid, inode->i_version);
    + fi->forget_req = NULL;
    + }
    +}
    +
    +static void fuse_put_super(struct super_block *sb)
    +{
    + struct fuse_conn *fc = SB_FC(sb);
    +
    + spin_lock(&fuse_lock);
    + fc->sb = NULL;
    + fc->uid = 0;
    + /* Flush all readers on this fs */
    + wake_up_all(&fc->waitq);
    + fuse_release_conn(fc);
    + SB_FC(sb) = NULL;
    + spin_unlock(&fuse_lock);
    +}
    +
    +enum {
    + OPT_FD,
    + OPT_ROOTMODE,
    + OPT_UID,
    + OPT_ERR
    +};
    +
    +static match_table_t tokens = {
    + {OPT_FD, "fd=%u"},
    + {OPT_ROOTMODE, "rootmode=%o"},
    + {OPT_UID, "uid=%u"},
    + {OPT_ERR, NULL}
    +};
    +
    +static int parse_fuse_opt(char *opt, struct fuse_mount_data *d)
    +{
    + char *p;
    + memset(d, 0, sizeof(struct fuse_mount_data));
    + d->fd = -1;
    +
    + while ((p = strsep(&opt, ",")) != NULL) {
    + int token;
    + int value;
    + substring_t args[MAX_OPT_ARGS];
    + if (!*p)
    + continue;
    +
    + token = match_token(p, tokens, args);
    + switch (token) {
    + case OPT_FD:
    + if (match_int(&args[0], &value))
    + return 0;
    + d->fd = value;
    + break;
    +
    + case OPT_ROOTMODE:
    + if (match_octal(&args[0], &value))
    + return 0;
    + d->rootmode = value;
    + break;
    +
    + case OPT_UID:
    + if (match_int(&args[0], &value))
    + return 0;
    + d->uid = value;
    + break;
    +
    + default:
    + return 0;
    + }
    + }
    + if (d->fd == -1)
    + return 0;
    +
    + return 1;
    +}
    +
    +static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt)
    +{
    + struct fuse_conn *fc = SB_FC(mnt->mnt_sb);
    +
    + seq_printf(m, ",uid=%u", fc->uid);
    + 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->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);
    + 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;
    +
    + if (file->f_op != &fuse_dev_operations) {
    + printk("FUSE: bad communication file descriptor\n");
    + return NULL;
    + }
    + fc = new_conn();
    + if (fc == NULL) {
    + printk("FUSE: failed to allocate connection data\n");
    + return NULL;
    + }
    + 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;
    +}
    +
    +static struct inode *get_root_inode(struct super_block *sb, unsigned int mode)
    +{
    + struct fuse_attr attr;
    + memset(&attr, 0, sizeof(attr));
    +
    + attr.mode = mode;
    + attr.ino = FUSE_ROOT_ID;
    + return fuse_iget(sb, 1, 0, &attr, 0);
    +}
    +
    +static struct super_operations fuse_super_operations = {
    + .alloc_inode = fuse_alloc_inode,
    + .destroy_inode = fuse_destroy_inode,
    + .read_inode = fuse_read_inode,
    + .clear_inode = fuse_clear_inode,
    + .put_super = fuse_put_super,
    + .show_options = fuse_show_options,
    +};
    +
    +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;
    +
    + if (!parse_fuse_opt((char *) data, &d))
    + return -EINVAL;
    +
    + sb->s_blocksize = PAGE_CACHE_SIZE;
    + sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
    + sb->s_magic = FUSE_SUPER_MAGIC;
    + sb->s_op = &fuse_super_operations;
    + sb->s_maxbytes = MAX_LFS_FILESIZE;
    +
    + file = fget(d.fd);
    + if (!file)
    + return -EINVAL;
    +
    + fc = get_conn(file, sb);
    + fput(file);
    + if (fc == NULL)
    + return -EINVAL;
    +
    + fc->uid = d.uid;
    +
    + SB_FC(sb) = fc;
    +
    + root = get_root_inode(sb, d.rootmode);
    + if (root == NULL) {
    + printk("fuse_read_super: failed to get root inode\n");
    + goto err;
    + }
    +
    + sb->s_root = d_alloc_root(root);
    + if (!sb->s_root) {
    + iput(root);
    + goto err;
    + }
    +
    + return 0;
    +
    + err:
    + spin_lock(&fuse_lock);
    + fc->sb = NULL;
    + fuse_release_conn(fc);
    + spin_unlock(&fuse_lock);
    + SB_FC(sb) = NULL;
    + return -EINVAL;
    +}
    +
    +static struct super_block *fuse_get_sb(struct file_system_type *fs_type,
    + int flags, const char *dev_name,
    + void *raw_data)
    +{
    + return get_sb_nodev(fs_type, flags, raw_data, fuse_read_super);
    +}
    +
    +static struct file_system_type fuse_fs_type = {
    + .owner = THIS_MODULE,
    + .name = "fuse",
    + .get_sb = fuse_get_sb,
    + .kill_sb = kill_anon_super,
    +};
    +
    +static void fuse_inode_init_once(void * foo, kmem_cache_t * cachep,
    + unsigned long flags)
    +{
    + struct inode * inode = foo;
    +
    + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
    + SLAB_CTOR_CONSTRUCTOR)
    + inode_init_once(inode);
    +}
    +
    +int fuse_fs_init(void)
    +{
    + int err;
    +
    + err = register_filesystem(&fuse_fs_type);
    + if (err)
    + printk("fuse: failed to register filesystem\n");
    + else {
    + fuse_inode_cachep = kmem_cache_create("fuse_inode",
    + sizeof(struct inode) + sizeof(struct fuse_inode) ,
    + 0, SLAB_HWCACHE_ALIGN,
    + fuse_inode_init_once, NULL);
    + if (!fuse_inode_cachep) {
    + unregister_filesystem(&fuse_fs_type);
    + err = -ENOMEM;
    + }
    + }
    +
    + return err;
    +}
    +
    +void fuse_fs_cleanup(void)
    +{
    + unregister_filesystem(&fuse_fs_type);
    + kmem_cache_destroy(fuse_inode_cachep);
    +}
    --- linux-2.6.10-rc2/fs/fuse/util.c 1970-01-01 01:00:00.000000000 +0100
    +++ linux-2.6.10-rc2-fuse/fs/fuse/util.c 2004-11-20 22:41:18.000000000 +0100
    @@ -0,0 +1,53 @@
    +/*
    + FUSE: Filesystem in Userspace
    + Copyright (C) 2001-2004 Miklos Szeredi <miklos@szeredi.hu>
    +
    + This program can be distributed under the terms of the GNU GPL.
    + See the file COPYING.
    +*/
    +
    +#include "fuse_i.h"
    +
    +#include <linux/init.h>
    +#include <linux/module.h>
    +
    +MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
    +MODULE_DESCRIPTION("Filesystem in Userspace");
    +MODULE_LICENSE("GPL");
    +
    +spinlock_t fuse_lock;
    +
    +int __init fuse_init(void)
    +{
    + int res;
    +
    + printk("fuse init (API version %i.%i)\n",
    + FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
    +
    + spin_lock_init(&fuse_lock);
    + 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 __exit fuse_exit(void)
    +{
    + printk(KERN_DEBUG "fuse exit\n");
    +
    + fuse_fs_cleanup();
    + fuse_dev_cleanup();
    +}
    +
    +module_init(fuse_init);
    +module_exit(fuse_exit);
    --- linux-2.6.10-rc2/fs/fuse/Makefile 1970-01-01 01:00:00.000000000 +0100
    +++ linux-2.6.10-rc2-fuse/fs/fuse/Makefile 2004-11-20 22:56:24.000000000 +0100
    @@ -0,0 +1,7 @@
    +#
    +# Makefile for the linux fuse routines.
    +#
    +
    +obj-$(CONFIG_FUSE) += fuse.o
    +
    +fuse-objs := dev.o dir.o inode.o util.o
    --- linux-2.6.10-rc2/include/linux/fuse.h 1970-01-01 01:00:00.000000000 +0100
    +++ linux-2.6.10-rc2-fuse/include/linux/fuse.h 2004-11-20 22:56:25.000000000 +0100
    @@ -0,0 +1,103 @@
    +/*
    + FUSE: Filesystem in Userspace
    + Copyright (C) 2001-2004 Miklos Szeredi <miklos@szeredi.hu>
    +
    + This program can be distributed under the terms of the GNU GPL.
    + See the file COPYING.
    +*/
    +
    +/* This file defines the kernel interface of FUSE */
    +
    +/** Version number of this interface */
    +#define FUSE_KERNEL_VERSION 4
    +
    +/** Minor version number of this interface */
    +#define FUSE_KERNEL_MINOR_VERSION 2
    +
    +/** The node ID of the root inode */
    +#define FUSE_ROOT_ID 1
    +
    +/** Opening this will yield a new control file */
    +#define FUSE_DEV "/dev/fuse"
    +
    +/** The file containing the version in the form MAJOR.MINOR */
    +#define FUSE_VERSION_FILE "/sys/fs/fuse/version"
    +
    +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;
    +};
    +
    +enum fuse_opcode {
    + FUSE_LOOKUP = 1,
    + FUSE_FORGET = 2, /* no reply */
    + /* FUSE_GETATTR = 3, */
    + /* FUSE_SETATTR = 4, */
    + /* FUSE_READLINK = 5, */
    + /* FUSE_SYMLINK = 6, */
    + /* FUSE_GETDIR = 7, */
    + /* FUSE_MKNOD = 8, */
    + /* FUSE_MKDIR = 9, */
    + /* FUSE_UNLINK = 10, */
    + /* FUSE_RMDIR = 11, */
    + /* FUSE_RENAME = 12, */
    + /* FUSE_LINK = 13, */
    + /* FUSE_OPEN = 14, */
    + /* FUSE_READ = 15, */
    + /* FUSE_WRITE = 16, */
    + /* FUSE_STATFS = 17, */
    + /* FUSE_RELEASE = 18, */
    + /* FUSE_INVALIDATE = 19, */
    + /* FUSE_FSYNC = 20, */
    + /* FUSE_SETXATTR = 21, */
    + /* FUSE_GETXATTR = 22, */
    + /* FUSE_LISTXATTR = 23, */
    + /* FUSE_REMOVEXATTR = 24, */
    + /* FUSE_FLUSH = 25, */
    +};
    +
    +/* Conservative buffer size for the client */
    +#define FUSE_MAX_IN 8192
    +
    +#define FUSE_NAME_MAX 1024
    +
    +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;
    + struct fuse_attr attr;
    +};
    +
    +struct fuse_forget_in {
    + int version;
    +};
    +
    +struct fuse_in_header {
    + int unique;
    + enum fuse_opcode opcode;
    + unsigned long nodeid;
    + unsigned int uid;
    + unsigned int gid;
    + unsigned int pid;
    +};
    +
    +struct fuse_out_header {
    + int unique;
    + int error;
    +};
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/
    \
     
     \ /
      Last update: 2005-03-22 14:08    [W:0.114 / U:31.212 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site