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