CC = gcc
-
-KCFLAGS = -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -pipe
-KCPPFLAGS = -I /lib/modules/`uname -r`/build/include/ -D__KERNEL__ -DMODULE -D_LOOSE_KERNEL_NAMES
-
CFLAGS = -Wall -W -g `glib-config --cflags`
LDFLAGS = `glib-config --libs`
-CPPFLAGS =
-
-all: fuse.o fusemount
+CPPFLAGS = -Iinclude
-dev.o: dev.c
- $(CC) $(KCFLAGS) $(KCPPFLAGS) -c dev.c
-inode.o: inode.c
- $(CC) $(KCFLAGS) $(KCPPFLAGS) -c inode.c
-dir.o: dir.c
- $(CC) $(KCFLAGS) $(KCPPFLAGS) -c dir.c
+all: kernel/fuse.o fusepro
-util.o: util.c
- $(CC) $(KCFLAGS) $(KCPPFLAGS) -c util.c
+kernel/fuse.o: FORCE
+ make -C kernel fuse.o
-fuse_objs = dev.o inode.o dir.o util.o
+lib/libfuse.a: FORCE
+ make -C lib libfuse.a
-fuse.o: $(fuse_objs)
- ld -r -o fuse.o $(fuse_objs)
-
-fusemount: fusemount.o
+fusepro: fusepro.o lib/libfuse.a
clean:
+ make -C kernel clean
+ make -C lib clean
rm -f *.o
- rm -f fusemount
+ rm -f fusepro
rm -f *~
+
+FORCE:
+++ /dev/null
-/*
- 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 ICSIZE sizeof(struct fuse_in_common)
-#define OCSIZE sizeof(struct fuse_out_common)
-
-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(!req->done) {
- 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;
-}
-
-void request_send(struct fuse_conn *fc, struct fuse_in *in,
- struct fuse_out *out)
-{
- int ret;
- struct fuse_req *req;
-
- req = kmalloc(sizeof(*req), GFP_KERNEL);
- if(req == NULL) {
- out->result = -ENOMEM;
- return;
- }
-
- req->in = in;
- req->out = out;
- req->done = 0;
- init_waitqueue_head(&req->waitq);
-
- spin_lock(&fuse_lock);
- if(fc->file == NULL) {
- ret = -ENOTCONN;
- goto out;
- }
-
- req->in->h.unique = fc->reqctr ++;
- list_add_tail(&req->list, &fc->pending);
- fc->outstanding ++;
- /* FIXME: Wait until the number of outstanding requests drops
- below a certain level */
- wake_up(&fc->waitq);
- ret = request_wait_answer(req);
- fc->outstanding --;
- list_del(&req->list);
-
- out:
- kfree(req);
- spin_unlock(&fuse_lock);
-
- if(ret)
- out->result = ret;
- else if (out->result <= -512 || out->result > 0) {
- printk("Bad result from client: %i\n", out->result);
- out->result = -EPROTO;
- }
-}
-
-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)
-{
- ssize_t ret;
- struct fuse_conn *fc = file->private_data;
- struct fuse_req *req;
- size_t size;
-
- spin_lock(&fuse_lock);
- ret = request_wait(fc);
- if(ret)
- goto err;
-
- req = list_entry(fc->pending.next, struct fuse_req, list);
- size = ICSIZE + req->in->argsize;
-
- ret = -EPROTO;
- if(nbytes < size) {
- printk("fuse_dev_read: buffer too small (%i)\n", size);
- goto err;
- }
-
- list_del(&req->list);
- list_add_tail(&req->list, &fc->processing);
- spin_unlock(&fuse_lock);
-
- if(copy_to_user(buf, &req->in->c, ICSIZE) ||
- copy_to_user(buf + ICSIZE, req->in->arg, req->in->argsize))
- 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->in->c.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)
-{
- struct fuse_conn *fc = file->private_data;
- struct fuse_req *req;
- struct fuse_out_common oc;
- char *tmpbuf;
-
- if(nbytes > 2 * PAGE_SIZE) {
- printk("fuse_dev_write: write is too long\n");
- return -EPROTO;
- }
-
- tmpbuf = (char *) __get_free_pages(GFP_KERNEL, 1);
- if(!tmpbuf)
- return -ENOMEM;
-
- if(copy_from_user(tmpbuf, buf, nbytes))
- return -EFAULT;
-
- spin_lock(&fuse_lock);
- req = request_find(fc, oc.unique);
-
- if(req == NULL)
- printk("fuse_dev_write[%i]: unknown request: %i", fc->id,
- oc.unique);
-
-
-
- else {
- struct fuse_outparam *out = ¶m.u.o;
- if(req->param.u.i.opcode == FUSE_OPEN && out->result == 0)
- out->u.open_internal.file = fget(out->u.open.fd);
-
- req->param = param;
- req->done = 1;
- wake_up(&req->waitq);
- }
- spin_unlock(&fuse_lock);
-
- return nbytes;
-}
-
-
-static unsigned int fuse_dev_poll(struct file *file, poll_table *wait)
-{
- struct fuse_conn *fc = file->private_data;
- unsigned int mask = POLLOUT | POLLWRNORM;
-
- 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->outstanding = 0;
- fc->reqctr = 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);
- req->done = 1;
- req->param.u.o.result = -ECONNABORTED;
- wake_up(&req->waitq);
- }
-}
-
-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);
- 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:
- */
+++ /dev/null
-/*
- 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
- 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_inparam in;
- struct fuse_outparam out;
- struct inode *inode;
-
- in.opcode = FUSE_LOOKUP;
- in.ino = dir->i_ino;
- strcpy(in.u.lookup.name, entry->d_name.name);
-
- request_send(fc, &in, &out);
-
- if(out.result)
- return ERR_PTR(out.result);
-
- inode = iget(dir->i_sb, out.u.lookup.ino);
- if(!inode)
- return ERR_PTR(-ENOMEM);
-
- init_inode(inode, &out.u.lookup.attr);
-
- d_add(entry, inode);
- return NULL;
-}
-
-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_inparam in;
- struct fuse_outparam out;
-
- in.opcode = FUSE_GETATTR;
- in.ino = inode->i_ino;
-
- request_send(fc, &in, &out);
-
- if(out.result == 0)
- change_attributes(inode, &out.u.getattr.attr);
-
- return out.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 err;
- if(dirent->namelen > NAME_MAX) {
- printk("fuse_readdir: name too long\n");
- ret = -EPROTO;
- goto out;
- }
- if(reclen > nbytes)
- break;
-
- err = filldir(dstbuf, dirent->name, dirent->namelen,
- file->f_pos, dirent->ino, dirent->type);
- if(err) {
- ret = err;
- break;
- }
- p += reclen;
- file->f_pos += reclen;
- nbytes -= reclen;
- ret ++;
- }
-
- 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;
- struct fuse_out out;
- unsigned long page;
-
- page = __get_free_page(GFP_KERNEL);
- if(!page)
- return -ENOMEM;
-
- in.c.opcode = FUSE_READLINK;
- in.c.ino = inode->i_ino;
- in.argsize = 0;
- out.arg = (void *) page;
- out.argsize = PAGE_SIZE;
-
- request_send(fc, &in, &out);
- if(out.c.result) {
- __free_page(page);
- return out.c.result;
- }
-
- *bufp = (char *) page;
- (*bufp)[PAGE_SIZE - 1] = 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_open(struct inode *inode, struct file *file)
-{
- struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
- struct fuse_inparam in;
- struct fuse_outparam out;
- struct file *cfile = NULL;
-
- in.opcode = FUSE_OPEN;
- in.ino = inode->i_ino;
- in.u.open.flags = file->f_flags & ~O_EXCL;
-
- request_send(fc, &in, &out);
-
- if(out.result == 0) {
- struct inode *inode;
- cfile = out.u.open_internal.file;
- if(!cfile) {
- printk("fuse_open: invalid container file\n");
- return -EPROTO;
- }
- inode = cfile->f_dentry->d_inode;
- if(!S_ISREG(inode->i_mode)) {
- printk("fuse_open: container is not a regular file\n");
- fput(cfile);
- return -EPROTO;
- }
-
- file->private_data = cfile;
- }
-
- return out.result;
-}
-
-static int fuse_release(struct inode *inode, struct file *file)
-{
- struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
- struct file *cfile = file->private_data;
- struct fuse_inparam in;
- struct fuse_outparam out;
-
- if(cfile)
- fput(cfile);
-
- in.opcode = FUSE_RELEASE;
- request_send(fc, &in, &out);
-
- return out.result;
-}
-
-static struct inode_operations fuse_dir_inode_operations =
-{
- lookup: fuse_lookup,
- permission: fuse_permission,
- revalidate: fuse_revalidate,
-};
-
-static struct file_operations fuse_dir_operations = {
- read: generic_read_dir,
- readdir: fuse_readdir,
- open: fuse_open,
- release: fuse_release,
-};
-
-static struct inode_operations fuse_file_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;
-}
-
-/*
- * Local Variables:
- * indent-tabs-mode: t
- * c-basic-offset: 8
- * End:
- */
+++ /dev/null
-/*
- 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/limits.h>
-
-#define FUSE_MOUNT_VERSION 1
-
-struct fuse_mount_data {
- int version;
- int fd;
-};
-
-#define FUSE_ROOT_INO 1
-
-struct fuse_attr {
- unsigned short mode;
- unsigned short nlink;
- unsigned short uid;
- unsigned short gid;
- unsigned short rdev;
- unsigned long size;
- unsigned long blksize;
- unsigned long blocks;
- unsigned long atime;
- unsigned long mtime;
- unsigned long ctime;
-};
-
-enum fuse_opcode {
- FUSE_LOOKUP,
- FUSE_GETATTR,
- FUSE_READLINK,
- FUSE_OPEN,
- FUSE_RELEASE,
-};
-
-/* Conservative buffer size for the client */
-#define FUSE_MAX_IN 8192
-
-struct fuse_in_open {
- int flag;
-};
-
-struct fuse_out_open {
- int fd;
-};
-
-struct fuse_in_lookup {
- char name[NAME_MAX + 1];
-};
-
-struct fuse_out_lookup {
- unsigned long ino;
- struct fuse_attr attr;
-};
-
-struct fuse_out_getattr {
- struct fuse_attr attr;
-};
-
-struct fuse_out_readlink {
- char link[PATH_MAX + 1];
-};
-
-struct fuse_in_common {
- int unique;
- enum fuse_opcode opcode;
- unsigned long ino;
-};
-
-struct fuse_out_common {
- int unique;
- int result;
-};
-
-struct fuse_in {
- struct fuse_in_common c;
- size_t argsize;
- void *arg;
-};
-
-struct fuse_out {
- struct fuse_out_common c;
- size_t argsize;
- void *arg;
-};
-
-struct fuse_dirent {
- unsigned long ino;
- unsigned short namelen;
- unsigned char type;
- char name[NAME_MAX + 1];
-};
-
-#define FUSE_NAME_OFFSET ((size_t) ((struct fuse_dirent *) 0)->name)
-#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(long) - 1) & ~(sizeof(long) - 1))
-#define FUSE_DIRENT_SIZE(d) \
- FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
-
-/*
- * Local Variables:
- * indent-tabs-mode: t
- * c-basic-offset: 8
- * End:
- */
+++ /dev/null
-/*
- 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.h"
-
-#include <linux/fs.h>
-#include <linux/list.h>
-#include <linux/spinlock.h>
-
-#define FUSE_VERSION "0.1"
-
-/**
- * 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 outstanding requests */
- int outstanding;
-
- /** 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 input parameters */
- struct fuse_in *in;
-
- /** The request result */
- struct fuse_out *out;
-
- /** The file returned by open */
- struct file *file;
-
- /** The request wait queue */
- wait_queue_head_t waitq;
-
- /** True if the request is finished */
- int done;
-};
-
-struct fuse_out_open_internal {
- file *file;
-};
-
-
-/**
- * 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);
-
-/**
- * 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
- */
+++ /dev/null
-/*
- AVFS: A Virtual File System Library
- Copyright (C) 1998-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.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <signal.h>
-#include <dirent.h>
-#include <assert.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/mount.h>
-#include <mntent.h>
-#include <glib.h>
-
-static char *mount_dir;
-
-const char *basedir = "/tmp/pro";
-
-struct node {
- char *name;
- unsigned long ino;
-};
-
-static GNode *root;
-static GHashTable *nodetab;
-static unsigned long inoctr = FUSE_ROOT_INO;
-
-static GNode *new_node(const char *name, unsigned long ino)
-{
- struct node *node = g_new0(struct node, 1);
- GNode *gn = g_node_new(node);
-
- node->name = g_strdup(name);
- node->ino = ino;
-
- return gn;
-}
-
-static unsigned long find_node(unsigned long ino, const char *name)
-{
- GNode *cn;
- GNode *pn = g_hash_table_lookup(nodetab, (gpointer) ino);
- if(pn == NULL) {
- fprintf(stderr, "Can't find parent node %li\n", ino);
- return 0;
- }
-
- for(cn = pn->children; cn != NULL; cn = cn->next) {
- struct node *node = (struct node *) cn->data;
- if(strcmp(node->name, name) == 0)
- return node->ino;
- }
-
- do inoctr++;
- while(!inoctr && g_hash_table_lookup(nodetab, (gpointer) ino) != NULL);
-
- cn = new_node(name, inoctr);
- g_node_insert(pn, -1, cn);
- g_hash_table_insert(nodetab, (gpointer) inoctr, cn);
-
- return inoctr;
-}
-
-static char *real_path(unsigned long ino)
-{
- GString *s;
- char *ss;
- GNode *gn = g_hash_table_lookup(nodetab, (gpointer) ino);
-
- if(gn == NULL) {
- fprintf(stderr, "Can't find node %li\n", ino);
- return NULL;
- }
-
- s = g_string_new("");
- for(; gn != NULL; gn = gn->parent) {
- g_string_prepend(s, ((struct node *) gn->data)->name);
- g_string_prepend_c(s, '/');
- }
- g_string_prepend(s, basedir);
- ss = s->str;
- g_string_free(s, FALSE);
-
- return ss;
-}
-
-static int get_dir(unsigned long dir)
-{
- int dirfd;
- struct fuse_dirent dirent;
- DIR *dp;
- struct dirent *de;
- size_t reclen;
- char *path;
-
- path = real_path(dir);
- if(path == NULL)
- return -ENOENT;
-
- dp = opendir(path);
- g_free(path);
- if(dp == NULL) {
- perror(path);
- return -errno;
- }
- dirfd = open("/tmp/dirtmp", O_RDWR | O_TRUNC | O_CREAT, 0600);
- if(dirfd == -1) {
- perror("/tmp/dirtmp");
- exit(1);
- }
- while((de = readdir(dp)) != NULL) {
- unsigned long ino = find_node(dir, de->d_name);
- assert(ino != 0);
-
- dirent.ino = ino;
- dirent.namelen = strlen(de->d_name);
- assert(dirent.namelen <= NAME_MAX);
- strcpy(dirent.name, de->d_name);
- dirent.type = de->d_type;
-
- reclen = FUSE_DIRENT_SIZE(&dirent);
- write(dirfd, &dirent, reclen);
- }
- closedir(dp);
-
- return dirfd;
-}
-
-static int get_attributes(unsigned long ino, struct fuse_attr *attr)
-{
- char *path;
- struct stat buf;
- int res;
-
- path = real_path(ino);
- if(path == NULL)
- return -ENOENT;
-
- res = stat(path, &buf);
- g_free(path);
- if(res == -1)
- return -errno;
-
- attr->mode = buf.st_mode;
- attr->nlink = buf.st_nlink;
- attr->uid = buf.st_uid;
- attr->gid = buf.st_gid;
- attr->rdev = buf.st_rdev;
- attr->size = buf.st_size;
- attr->blksize = buf.st_blksize;
- attr->blocks = buf.st_blocks;
- attr->atime = buf.st_atime;
- attr->mtime = buf.st_mtime;
- attr->ctime = buf.st_ctime;
-
- return 0;
-}
-
-static void loop(int devfd)
-{
- int res;
- struct fuse_param param;
- struct fuse_outparam out;
- struct fuse_inparam in;
- int dirfd;
-
- while(1) {
- res = read(devfd, ¶m, sizeof(param));
- if(res == -1) {
- perror("read");
- exit(1);
- }
-
- printf("unique: %i, opcode: %i\n", param.unique, param.u.i.opcode);
-
- dirfd = -1;
- in = param.u.i;
- switch(in.opcode) {
- case FUSE_LOOKUP:
- out.u.lookup.ino = find_node(in.ino, in.u.lookup.name);
- if(out.u.lookup.ino == 0)
- out.result = -ENOENT;
- else
- out.result = get_attributes(out.u.lookup.ino,
- &out.u.lookup.attr);
- break;
-
- case FUSE_GETATTR:
- out.result = get_attributes(in.ino, &out.u.getattr.attr);
- break;
-
- case FUSE_OPEN:
- dirfd = get_dir(in.ino);
- if(dirfd >= 0) {
- out.u.open.fd = dirfd;
- out.result = 0;
- }
- else
- out.result = dirfd;
- break;
-
- case FUSE_RELEASE:
- out.result = 0;
- break;
-
- default:
- out.result = -EOPNOTSUPP;
- }
- param.u.o = out;
-
- res = write(devfd, ¶m, sizeof(param));
- if(res == -1) {
- perror("write");
- exit(1);
- }
- if(dirfd != -1) {
- close(dirfd);
- unlink("/tmp/dirtmp");
- }
- }
-}
-
-static int mount_fuse(const char *dev, const char *dir, int devfd)
-{
- int res;
- const char *type;
- FILE *fd;
- struct mntent ent;
- struct fuse_mount_data data;
-
- data.version = FUSE_MOUNT_VERSION;
- data.fd = devfd;
-
- type = "fuse";
- res = mount(dev, dir, type, MS_MGC_VAL | MS_NOSUID | MS_NODEV, &data);
-
- if(res == -1) {
- fprintf(stderr, "mount failed: %s\n", strerror(errno));
- return -1;
- }
-
- fd = setmntent("/etc/mtab", "a");
- if(fd == NULL) {
- fprintf(stderr, "setmntent(\"/etc/mtab\") failed: %s\n",
- strerror(errno));
- return -1;
- }
-
- ent.mnt_fsname = (char *) dev;
- ent.mnt_dir = (char *) dir;
- ent.mnt_type = (char *) type;
- ent.mnt_opts = "rw,nosuid,nodev";
- ent.mnt_freq = 0;
- ent.mnt_passno = 0;
- res = addmntent(fd, & ent);
- if(res != 0)
- fprintf(stderr, "addmntent() failed: %s\n", strerror(errno));
-
- endmntent(fd);
-
- return 0;
-}
-
-int unmount_fuse(const char *dir)
-{
- int res;
- FILE *fdold, *fdnew;
- struct mntent *entp;
-
- res = umount(dir);
-
- if(res == -1) {
- fprintf(stderr, "umount failed: %s\n", strerror(errno));
- return -1;
- }
-
- fdold = setmntent("/etc/mtab", "r");
- if(fdold == NULL) {
- fprintf(stderr, "setmntent(\"/etc/mtab\") failed: %s\n",
- strerror(errno));
- return -1;
- }
-
- fdnew = setmntent("/etc/mtab~", "w");
- if(fdnew == NULL) {
- fprintf(stderr, "setmntent(\"/etc/mtab~\") failed: %s\n",
- strerror(errno));
- return -1;
- }
-
- do {
- entp = getmntent(fdold);
- if(entp != NULL) {
- if(strcmp(entp->mnt_dir, dir) != 0) {
- res = addmntent(fdnew, entp);
- if(res != 0) {
- fprintf(stderr, "addmntent() failed: %s\n",
- strerror(errno));
- }
- }
- }
- } while(entp != NULL);
-
- endmntent(fdold);
- endmntent(fdnew);
-
- res = rename("/etc/mtab~", "/etc/mtab");
- if(res == -1) {
- fprintf(stderr, "rename(\"/etc/mtab~\", \"/etc/mtab\") failed: %s\n",
- strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-void cleanup()
-{
- unmount_fuse(mount_dir);
-}
-
-
-void exit_handler()
-{
- exit(0);
-}
-
-void set_signal_handlers()
-{
- struct sigaction sa;
-
- sa.sa_handler = exit_handler;
- sigemptyset(&(sa.sa_mask));
- sa.sa_flags = 0;
-
- if (sigaction(SIGHUP, &sa, NULL) == -1 ||
- sigaction(SIGINT, &sa, NULL) == -1 ||
- sigaction(SIGTERM, &sa, NULL) == -1) {
-
- perror("Cannot set exit signal handlers");
- exit(1);
- }
-
- sa.sa_handler = SIG_IGN;
-
- if(sigaction(SIGPIPE, &sa, NULL) == -1) {
- perror("Cannot set ignored signals");
- exit(1);
- }
-}
-
-
-int main(int argc, char *argv[])
-{
- const char *dev;
- int devfd;
-
- if(argc < 3) {
- fprintf(stderr, "usage: %s dev dir\n", argv[0]);
- exit(1);
- }
-
- dev = argv[1];
- mount_dir = argv[2];
-
- devfd = open(dev, O_RDWR);
- if(devfd == -1) {
- fprintf(stderr, "failed to open %s: %s\n", dev, strerror(errno));
- exit(1);
- }
-
- mount_fuse(dev, mount_dir, devfd);
-
- set_signal_handlers();
- atexit(cleanup);
-
- root = new_node("/", FUSE_ROOT_INO);
- nodetab = g_hash_table_new(NULL, NULL);
- g_hash_table_insert(nodetab, (gpointer) FUSE_ROOT_INO, root);
-
- loop(devfd);
-
- return 0;
-}
-
-
-
--- /dev/null
+#include <fuse.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <signal.h>
+
+static struct fuse *pro_fuse;
+
+static int pro_getattr(const char *path, struct stat *stbuf)
+{
+ int res;
+
+ res = lstat(path, stbuf);
+ if(res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int pro_readlink(const char *path, char *buf, size_t size)
+{
+ int res;
+
+ res = readlink(path, buf, size - 1);
+ if(res == -1)
+ return -errno;
+
+ buf[res] = '\0';
+ return 0;
+}
+
+
+static int pro_getdir(const char *path, struct fuse_dh *h, dirfiller_t filler)
+{
+ DIR *dp;
+ struct dirent *de;
+ int res;
+
+ dp = opendir(path);
+ if(dp == NULL)
+ return -errno;
+
+ while((de = readdir(dp)) != NULL) {
+ res = filler(h, de->d_name, de->d_type);
+ if(res != 0)
+ break;
+ }
+
+ closedir(dp);
+ return res;
+}
+
+static int pro_mknod(const char *path, int mode, int rdev)
+{
+ int res;
+
+ res = mknod(path, mode, rdev);
+ if(res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static void exit_handler()
+{
+ exit(0);
+}
+
+static void set_signal_handlers()
+{
+ struct sigaction sa;
+
+ sa.sa_handler = exit_handler;
+ sigemptyset(&(sa.sa_mask));
+ sa.sa_flags = 0;
+
+ if (sigaction(SIGHUP, &sa, NULL) == -1 ||
+ sigaction(SIGINT, &sa, NULL) == -1 ||
+ sigaction(SIGTERM, &sa, NULL) == -1) {
+
+ perror("Cannot set exit signal handlers");
+ exit(1);
+ }
+
+ sa.sa_handler = SIG_IGN;
+
+ if(sigaction(SIGPIPE, &sa, NULL) == -1) {
+ perror("Cannot set ignored signals");
+ exit(1);
+ }
+}
+
+static void cleanup()
+{
+ fuse_unmount(pro_fuse);
+ fuse_destroy(pro_fuse);
+}
+
+static struct fuse_operations pro_oper = {
+ getattr: pro_getattr,
+ readlink: pro_readlink,
+ getdir: pro_getdir,
+ mknod: pro_mknod,
+};
+
+int main(int argc, char *argv[])
+{
+ if(argc != 2) {
+ fprintf(stderr, "usage: %s mount_dir\n", argv[0]);
+ exit(1);
+ }
+
+ set_signal_handlers();
+ atexit(cleanup);
+
+ pro_fuse = fuse_new();
+ fuse_mount(pro_fuse, argv[1]);
+ fuse_set_operations(pro_fuse, &pro_oper);
+ fuse_loop(pro_fuse);
+
+ return 0;
+}
--- /dev/null
+/*
+ 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.
+*/
+
+/* This file defines the library interface of FUSE */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+struct fuse;
+struct fuse_dh;
+
+typedef int (*dirfiller_t) (struct fuse_dh *, const char *, int type);
+
+
+struct fuse_operations {
+ int (*getattr) (const char *path, struct stat *stbuf);
+ int (*readlink) (const char *path, char *buf, size_t size);
+ int (*mknod) (const char *path, int mode, int rdev);
+ int (*getdir) (const char *path, struct fuse_dh *h, dirfiller_t filler);
+};
+
+struct fuse *fuse_new();
+
+int fuse_mount(struct fuse *f, const char *dir);
+
+void fuse_set_operations(struct fuse *f, const struct fuse_operations *op);
+
+void fuse_loop(struct fuse *f);
+
+int fuse_unmount(struct fuse *f);
+
+void fuse_destroy(struct fuse *f);
--- /dev/null
+/*
+ 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.
+*/
+
+/* This file defines the kernel interface of FUSE */
+
+
+#define FUSE_MOUNT_VERSION 1
+
+struct fuse_mount_data {
+ int version;
+ int fd;
+};
+
+#define FUSE_ROOT_INO 1
+
+struct fuse_attr {
+ unsigned short mode;
+ unsigned short nlink;
+ unsigned short uid;
+ unsigned short gid;
+ unsigned short rdev;
+ unsigned long long size;
+ unsigned long blksize;
+ unsigned long blocks;
+ unsigned long atime;
+ unsigned long mtime;
+ unsigned long ctime;
+};
+
+enum fuse_opcode {
+ FUSE_LOOKUP = 1,
+ FUSE_FORGET,
+ FUSE_GETATTR,
+ FUSE_READLINK,
+ FUSE_GETDIR,
+ FUSE_MKNOD,
+};
+
+/* Conservative buffer size for the client */
+#define FUSE_MAX_IN 8192
+
+struct fuse_lookup_out {
+ unsigned long ino;
+ struct fuse_attr attr;
+};
+
+struct fuse_getattr_out {
+ struct fuse_attr attr;
+};
+
+struct fuse_getdir_out {
+ int fd;
+ void *file; /* Used by kernel only */
+};
+
+struct fuse_mknod_in {
+ unsigned short mode;
+ unsigned short rdev;
+ char name[1];
+};
+
+struct fuse_mknod_out {
+ unsigned long ino;
+ struct fuse_attr attr;
+};
+
+struct fuse_in_header {
+ int unique;
+ enum fuse_opcode opcode;
+ unsigned long ino;
+};
+
+struct fuse_out_header {
+ int unique;
+ int result;
+};
+
+struct fuse_dirent {
+ unsigned long ino;
+ unsigned short namelen;
+ unsigned char type;
+ char name[256];
+};
+
+#define FUSE_NAME_OFFSET ((unsigned int) ((struct fuse_dirent *) 0)->name)
+#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(long) - 1) & ~(sizeof(long) - 1))
+#define FUSE_DIRENT_SIZE(d) \
+ FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
+
+/*
+ * Local Variables:
+ * indent-tabs-mode: t
+ * c-basic-offset: 8
+ * End:
+ */
+++ /dev/null
-/*
- 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>
-
-#define FUSE_SUPER_MAGIC 0x65735546
-
-static void fuse_read_inode(struct inode *inode)
-{
- /* No op */
-}
-
-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,
- 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:
- */
-
--- /dev/null
+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 *~
--- /dev/null
+/*
+ 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:
+ */
--- /dev/null
+/*
+ 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:
+ */
--- /dev/null
+/*
+ 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
+ */
--- /dev/null
+/*
+ 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:
+ */
+
--- /dev/null
+/*
+ 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
+ */
--- /dev/null
+CC = gcc
+CFLAGS = -Wall -W -g `glib-config --cflags`
+LDFLAGS = `glib-config --libs`
+CPPFLAGS = -I../include
+
+
+all: libfuse.a
+
+libfuse_objs = mount.o fuse.o
+
+libfuse.a: $(libfuse_objs)
+ ar cr libfuse.a $(libfuse_objs)
+
+clean:
+ rm -f *.o *.a
+ rm -f *~
--- /dev/null
+/*
+ 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/fuse.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+
+static guint name_hash(const struct node *node)
+{
+ return g_str_hash(node->name) ^ node->parent;
+}
+
+static gint name_compare(const struct node *node1, const struct node *node2)
+{
+ return
+ node1->parent == node2->parent &&
+ strcmp(node1->name, node2->name) == 0;
+}
+
+static struct node *new_node(fino_t parent, const char *name)
+{
+ struct node *node = g_new0(struct node, 1);
+ node->name = strdup(name);
+ node->parent = parent;
+ return node;
+}
+
+static int free_node(struct node *node)
+{
+ g_free(node->name);
+ g_free(node);
+ return 1;
+}
+
+static inline struct node *get_node(fino_t ino)
+{
+ return (struct node *) ino;
+}
+
+static inline fino_t get_ino(struct node *node)
+{
+ return (fino_t) node;
+}
+
+
+static fino_t find_node(struct fuse *f, fino_t parent, char *name, int create)
+{
+ struct node *node;
+ struct node tmp;
+
+ tmp.name = name;
+ tmp.parent = parent;
+
+ node = g_hash_table_lookup(f->nametab, &tmp);
+ if(node != NULL)
+ return get_ino(node);
+
+ if(!create)
+ return (fino_t) -1;
+
+ node = new_node(parent, name);
+ g_hash_table_insert(f->nametab, node, node);
+ return get_ino(node);
+}
+
+static char *get_path(fino_t ino)
+{
+ GString *s;
+ char *ss;
+
+ s = g_string_new("");
+ if(ino == FUSE_ROOT_INO)
+ g_string_prepend_c(s, '/');
+ else {
+ struct node *node;
+ for(; ino != FUSE_ROOT_INO; ino = node->parent) {
+ node = get_node(ino);
+ g_string_prepend(s, node->name);
+ g_string_prepend_c(s, '/');
+ }
+ }
+
+ ss = s->str;
+ g_string_free(s, FALSE);
+
+ return ss;
+}
+
+static void remove_node(struct fuse *f, fino_t ino)
+{
+ struct node *node = get_node(ino);
+ g_hash_table_remove(f->nametab, node);
+ free_node(node);
+}
+
+
+static void convert_stat(struct stat *stbuf, struct fuse_attr *attr)
+{
+ attr->mode = stbuf->st_mode;
+ attr->nlink = stbuf->st_nlink;
+ attr->uid = stbuf->st_uid;
+ attr->gid = stbuf->st_gid;
+ attr->rdev = stbuf->st_rdev;
+ attr->size = stbuf->st_size;
+ attr->blksize = stbuf->st_blksize;
+ attr->blocks = stbuf->st_blocks;
+ attr->atime = stbuf->st_atime;
+ attr->mtime = stbuf->st_mtime;
+ attr->ctime = stbuf->st_ctime;
+}
+
+static int get_attributes(struct fuse *f, fino_t ino, struct fuse_attr *attr)
+{
+ char *path;
+ struct stat buf;
+ int res;
+
+ if(f->op.getattr == NULL)
+ return -ENOSYS;
+
+ path = get_path(ino);
+ res = f->op.getattr(path, &buf);
+ g_free(path);
+ if(res == 0)
+ convert_stat(&buf, attr);
+
+ return res;
+}
+
+static int read_link(struct fuse *f, fino_t ino, char *buf, size_t size)
+{
+ char *path;
+ int res;
+
+ if(f->op.readlink == NULL)
+ return -ENOSYS;
+
+ path = get_path(ino);
+ res = f->op.readlink(path, buf, size);
+ g_free(path);
+
+ return res;
+}
+
+static int fill_dir(struct fuse_dh *dh, char *name, int type)
+{
+ struct fuse_dirent dirent;
+ size_t reclen;
+ size_t res;
+
+ dirent.ino = find_node(dh->fuse, dh->dir, name, 0);
+ dirent.namelen = strlen(name);
+ strncpy(dirent.name, name, sizeof(dirent.name));
+ dirent.type = type;
+ reclen = FUSE_DIRENT_SIZE(&dirent);
+ res = fwrite(&dirent, reclen, 1, dh->fp);
+ if(res == 0) {
+ perror("writing directory file");
+ return -EIO;
+ }
+ return 0;
+}
+
+static int get_dir(struct fuse *f, fino_t ino, FILE *fp)
+{
+ char *path;
+ int res;
+ struct fuse_dh dh;
+
+ if(f->op.getdir == NULL)
+ return -ENOSYS;
+
+ dh.fuse = f;
+ dh.fp = fp;
+ dh.dir = ino;
+
+ path = get_path(ino);
+ res = f->op.getdir(path, &dh, (dirfiller_t) fill_dir);
+ g_free(path);
+
+ return res;
+}
+
+
+static void send_reply(struct fuse *f, struct fuse_in_header *in, int result,
+ void *arg, size_t argsize)
+{
+ int res;
+ char *outbuf;
+ size_t outsize;
+ struct fuse_out_header *out;
+
+ if(result > 0) {
+ fprintf(stderr, "positive result to operation %i : %i\n", in->opcode,
+ result);
+ result = -ERANGE;
+ }
+
+ if(result != 0)
+ argsize = 0;
+
+ outsize = sizeof(struct fuse_out_header) + argsize;
+ outbuf = (char *) g_malloc(outsize);
+ out = (struct fuse_out_header *) outbuf;
+ out->unique = in->unique;
+ out->result = result;
+ if(argsize != 0)
+ memcpy(outbuf + sizeof(struct fuse_out_header), arg, argsize);
+
+ printf(" unique: %i, result: %i (%s), outsize: %i\n", out->unique,
+ out->result, strerror(-out->result), outsize);
+
+ res = write(f->fd, outbuf, outsize);
+ if(res == -1)
+ perror("writing fuse device");
+
+ g_free(outbuf);
+}
+
+static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name)
+{
+ int res;
+ struct fuse_lookup_out arg;
+
+ arg.ino = find_node(f, in->ino, name, 1);
+ res = get_attributes(f, arg.ino, &arg.attr);
+
+ send_reply(f, in, res, &arg, sizeof(arg));
+}
+
+
+static void do_forget(struct fuse *f, unsigned long *inos, size_t num)
+{
+ size_t i;
+
+ for(i = 0; i < num; i++)
+ remove_node(f, inos[i]);
+}
+
+static void do_getattr(struct fuse *f, struct fuse_in_header *in)
+{
+ int res;
+ struct fuse_getattr_out arg;
+
+ res = get_attributes(f, in->ino, &arg.attr);
+ send_reply(f, in, res, &arg, sizeof(arg));
+}
+
+static void do_readlink(struct fuse *f, struct fuse_in_header *in)
+{
+ int res;
+ char link[PATH_MAX + 1];
+
+ res = read_link(f, in->ino, link, PATH_MAX + 1);
+ send_reply(f, in, res, link, res == 0 ? strlen(link) : 0);
+}
+
+static void do_mknod(struct fuse *f, struct fuse_in_header *in,
+ struct fuse_mknod_in *inarg)
+{
+ int res;
+ struct fuse_mknod_out outarg;
+
+ res = -ENOSYS;
+ if(f->op.mknod != NULL && f->op.getattr != NULL) {
+ char *path;
+ struct stat buf;
+
+ outarg.ino = find_node(f, in->ino, inarg->name, 1);
+ path = get_path(outarg.ino);
+ res = f->op.mknod(path, inarg->mode, inarg->rdev);
+ if(res == 0)
+ res = f->op.getattr(path, &buf);
+ g_free(path);
+
+ if(res == 0)
+ convert_stat(&buf, &outarg.attr);
+ else
+ remove_node(f, outarg.ino);
+ }
+ send_reply(f, in, res, &outarg, sizeof(outarg));
+}
+
+static void do_getdir(struct fuse *f, struct fuse_in_header *in)
+{
+ int res;
+ struct fuse_getdir_out arg;
+ FILE *fp = tmpfile();
+
+ res = get_dir(f, in->ino, fp);
+ fflush(fp);
+ arg.fd = fileno(fp);
+ send_reply(f, in, res, &arg, sizeof(arg));
+ fclose(fp);
+}
+
+void fuse_loop(struct fuse *f)
+{
+ int res;
+ char inbuf[FUSE_MAX_IN];
+ struct fuse_in_header *in = (struct fuse_in_header *) inbuf;
+ void *inarg = inbuf + sizeof(struct fuse_in_header);
+ size_t insize;
+ size_t argsize;
+
+ while(1) {
+ res = read(f->fd, inbuf, sizeof(inbuf));
+ if(res == -1) {
+ perror("reading fuse device");
+ continue;
+ }
+ insize = res;
+
+ if(insize < sizeof(struct fuse_in_header)) {
+ fprintf(stderr, "short read on fuse device\n");
+ continue;
+ }
+ printf("unique: %i, opcode: %i, ino: %li, insize: %i (%i)\n",
+ in->unique, in->opcode, in->ino, insize,
+ g_hash_table_size(f->nametab));
+
+ argsize = insize - sizeof(struct fuse_in_header);
+
+ switch(in->opcode) {
+ case FUSE_LOOKUP:
+ do_lookup(f, in, (char *) inarg);
+ break;
+
+ case FUSE_FORGET:
+ do_forget(f, (unsigned long *) inarg,
+ argsize / sizeof(unsigned long));
+ break;
+
+ case FUSE_GETATTR:
+ do_getattr(f, in);
+ break;
+
+ case FUSE_READLINK:
+ do_readlink(f, in);
+ break;
+
+ case FUSE_GETDIR:
+ do_getdir(f, in);
+ break;
+
+ case FUSE_MKNOD:
+ do_mknod(f, in, (struct fuse_mknod_in *) inarg);
+ break;
+
+ default:
+ fprintf(stderr, "Operation %i not implemented\n", in->opcode);
+ /* No need to send reply to async requests */
+ if(in->unique != 0)
+ send_reply(f, in, -ENOSYS, NULL, 0);
+ }
+ }
+}
+
+struct fuse *fuse_new()
+{
+ struct fuse *f = g_new0(struct fuse, 1);
+
+ f->fd = -1;
+ f->dir = NULL;
+ f->nametab = g_hash_table_new((GHashFunc) name_hash,
+ (GCompareFunc) name_compare);
+
+ return f;
+}
+
+
+void fuse_set_operations(struct fuse *f, const struct fuse_operations *op)
+{
+ f->op = *op;
+}
+
+void fuse_destroy(struct fuse *f)
+{
+ fuse_unmount(f);
+ g_hash_table_foreach_remove(f->nametab, (GHRFunc) free_node, NULL);
+ g_hash_table_destroy(f->nametab);
+ g_free(f);
+}
+
--- /dev/null
+/*
+ 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.h"
+#include <glib.h>
+#include <stdio.h>
+
+#define FUSE_DEV "/proc/fs/fuse/dev"
+
+typedef unsigned long fino_t;
+
+struct node {
+ char *name;
+ fino_t parent;
+};
+
+struct fuse {
+ char *dir;
+ int fd;
+ struct fuse_operations op;
+ GHashTable *nametab;
+};
+
+struct fuse_dh {
+ struct fuse *fuse;
+ fino_t dir;
+ FILE *fp;
+};
--- /dev/null
+/*
+ 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/fuse.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+#include <mntent.h>
+
+static int do_mount(const char *dev, const char *dir, const char *type, int fd)
+{
+ int res;
+ struct fuse_mount_data data;
+
+ data.version = FUSE_MOUNT_VERSION;
+ data.fd = fd;
+
+ res = mount(dev, dir, type, MS_MGC_VAL | MS_NOSUID | MS_NODEV, &data);
+ if(res == -1) {
+ perror("mount failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void add_mntent(const char *dev, const char *dir, const char *type)
+{
+ int res;
+ FILE *fp;
+ struct mntent ent;
+
+ fp = setmntent("/etc/mtab", "a");
+ if(fp == NULL) {
+ perror("setmntent");
+ return;
+ }
+
+ ent.mnt_fsname = (char *) dev;
+ ent.mnt_dir = (char *) dir;
+ ent.mnt_type = (char *) type;
+ ent.mnt_opts = "rw,nosuid,nodev";
+ ent.mnt_freq = 0;
+ ent.mnt_passno = 0;
+ res = addmntent(fp, & ent);
+ if(res != 0)
+ perror("addmntent");
+
+ endmntent(fp);
+
+}
+
+static void remove_mntent(const char *dir)
+{
+ int res;
+ FILE *fdold, *fdnew;
+ struct mntent *entp;
+
+ fdold = setmntent("/etc/mtab", "r");
+ if(fdold == NULL) {
+ perror("/etc/mtab");
+ return;
+ }
+
+ fdnew = setmntent("/etc/mtab~", "w");
+ if(fdnew == NULL) {
+ perror("/etc/mtab~");
+ return;
+ }
+
+ do {
+ entp = getmntent(fdold);
+ if(entp != NULL && strcmp(entp->mnt_dir, dir) != 0) {
+ res = addmntent(fdnew, entp);
+ if(res != 0)
+ perror("addmntent");
+ }
+ } while(entp != NULL);
+
+ endmntent(fdold);
+ endmntent(fdnew);
+
+ res = rename("/etc/mtab~", "/etc/mtab");
+ if(res == -1)
+ perror("renameing /etc/mtab~ to /etc/mtab");
+}
+
+int fuse_mount(struct fuse *f, const char *dir)
+{
+ int res;
+ const char *dev = FUSE_DEV;
+ const char *type = "fuse";
+
+ if(f->dir != NULL)
+ return 0;
+
+ f->dir = g_strdup(dir);
+ f->fd = open(dev, O_RDWR);
+ if(f->fd == -1) {
+ perror(dev);
+ return -1;
+ }
+
+ res = do_mount(dev, dir, type, f->fd);
+ if(res == -1)
+ return -1;
+
+ add_mntent(dev, dir, type);
+
+ return 0;
+}
+
+int fuse_unmount(struct fuse *f)
+{
+ int res;
+
+ if(f->dir == NULL)
+ return 0;
+
+ close(f->fd);
+ f->fd = -1;
+
+ res = umount(f->dir);
+ if(res == -1)
+ perror("umount failed");
+ else
+ remove_mntent(f->dir);
+
+ g_free(f->dir);
+ f->dir = NULL;
+
+ return res;
+}
+++ /dev/null
-/*
- 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
- */