From 85c74fcdfd9e67d411c3e1734b34effd0d73fa4d Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Sun, 28 Oct 2001 19:44:14 +0000 Subject: [PATCH] x --- Makefile | 33 +-- fusemount.c | 399 --------------------------------- fusepro.c | 125 +++++++++++ include/fuse.h | 37 +++ fuse.h => include/linux/fuse.h | 80 +++---- kernel/Makefile | 16 ++ dev.c => kernel/dev.c | 248 ++++++++++++++------ dir.c => kernel/dir.c | 204 +++++++++++------ fuse_i.h => kernel/fuse_i.h | 57 +++-- inode.c => kernel/inode.c | 66 ++++++ util.c => kernel/util.c | 0 lib/Makefile | 16 ++ lib/fuse.c | 393 ++++++++++++++++++++++++++++++++ lib/fuse_i.h | 33 +++ lib/mount.c | 142 ++++++++++++ 15 files changed, 1230 insertions(+), 619 deletions(-) delete mode 100644 fusemount.c create mode 100644 fusepro.c create mode 100644 include/fuse.h rename fuse.h => include/linux/fuse.h (56%) create mode 100644 kernel/Makefile rename dev.c => kernel/dev.c (59%) rename dir.c => kernel/dir.c (58%) rename fuse_i.h => kernel/fuse_i.h (71%) rename inode.c => kernel/inode.c (68%) rename util.c => kernel/util.c (100%) create mode 100644 lib/Makefile create mode 100644 lib/fuse.c create mode 100644 lib/fuse_i.h create mode 100644 lib/mount.c 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/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/fuse.h b/include/linux/fuse.h similarity index 56% rename from fuse.h rename to include/linux/fuse.h index 04faf9d..fb3aca0 100644 --- a/fuse.h +++ b/include/linux/fuse.h @@ -6,7 +6,8 @@ See the file COPYING. */ -#include +/* This file defines the kernel interface of FUSE */ + #define FUSE_MOUNT_VERSION 1 @@ -18,86 +19,75 @@ struct fuse_mount_data { #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; + 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, + FUSE_LOOKUP = 1, + FUSE_FORGET, FUSE_GETATTR, FUSE_READLINK, - FUSE_OPEN, - FUSE_RELEASE, + FUSE_GETDIR, + FUSE_MKNOD, }; /* Conservative buffer size for the client */ #define FUSE_MAX_IN 8192 -struct fuse_in_open { - int flag; +struct fuse_lookup_out { + unsigned long ino; + struct fuse_attr attr; }; -struct fuse_out_open { - int fd; +struct fuse_getattr_out { + struct fuse_attr attr; }; -struct fuse_in_lookup { - char name[NAME_MAX + 1]; +struct fuse_getdir_out { + int fd; + void *file; /* Used by kernel only */ }; -struct fuse_out_lookup { - unsigned long ino; - struct fuse_attr attr; +struct fuse_mknod_in { + unsigned short mode; + unsigned short rdev; + char name[1]; }; -struct fuse_out_getattr { +struct fuse_mknod_out { + unsigned long ino; struct fuse_attr attr; }; -struct fuse_out_readlink { - char link[PATH_MAX + 1]; -}; - -struct fuse_in_common { +struct fuse_in_header { int unique; enum fuse_opcode opcode; unsigned long ino; }; -struct fuse_out_common { +struct fuse_out_header { 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]; + char name[256]; }; -#define FUSE_NAME_OFFSET ((size_t) ((struct fuse_dirent *) 0)->name) +#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) 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/dev.c b/kernel/dev.c similarity index 59% rename from dev.c rename to kernel/dev.c index 16d8ad1..a91a176 100644 --- a/dev.c +++ b/kernel/dev.c @@ -14,8 +14,8 @@ #include #include -#define ICSIZE sizeof(struct fuse_in_common) -#define OCSIZE sizeof(struct fuse_out_common) +#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; @@ -26,7 +26,7 @@ static int request_wait_answer(struct fuse_req *req) DECLARE_WAITQUEUE(wait, current); add_wait_queue(&req->waitq, &wait); - while(!req->done) { + while(!list_empty(&req->list)) { set_current_state(TASK_INTERRUPTIBLE); if(signal_pending(current)) { ret = -EINTR; @@ -42,49 +42,123 @@ static int request_wait_answer(struct fuse_req *req) return ret; } -void request_send(struct fuse_conn *fc, struct fuse_in *in, - struct fuse_out *out) +static int request_check(struct fuse_req *req, struct fuse_out *outp) { - int ret; - struct fuse_req *req; + struct fuse_out_header *oh; + unsigned int size; - req = kmalloc(sizeof(*req), GFP_KERNEL); - if(req == NULL) { - out->result = -ENOMEM; - return; + 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; } - req->in = in; - req->out = out; - req->done = 0; - init_waitqueue_head(&req->waitq); + memcpy(&outp->h, oh, OHSIZE); + outp->argsize = size; + if(size) + memcpy(outp->arg, req->out + OHSIZE, size); - spin_lock(&fuse_lock); - if(fc->file == NULL) { - ret = -ENOTCONN; + 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; - 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); + + /* Async reqests are freed in fuse_dev_read() */ + if(!outp) + goto out_unlock; + ret = request_wait_answer(req); - fc->outstanding --; list_del(&req->list); + if(!ret) + ret = request_check(req, outp); - out: - kfree(req); + out_unlock_free: + request_free(req); + out_unlock: 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; - } + out: + if(outp) + outp->h.result = ret; } static int request_wait(struct fuse_conn *fc) @@ -113,31 +187,38 @@ static int request_wait(struct fuse_conn *fc) static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes, loff_t *off) { - ssize_t ret; + int ret; struct fuse_conn *fc = file->private_data; struct fuse_req *req; - size_t size; + 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 = ICSIZE + req->in->argsize; - - ret = -EPROTO; + size = req->insize; if(nbytes < size) { - printk("fuse_dev_read: buffer too small (%i)\n", 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); - list_add_tail(&req->list, &fc->processing); + if(req->outsize) + list_add_tail(&req->list, &fc->processing); + else + request_free(req); spin_unlock(&fuse_lock); - if(copy_to_user(buf, &req->in->c, ICSIZE) || - copy_to_user(buf + ICSIZE, req->in->arg, req->in->argsize)) + if(copy_to_user(buf, tmpbuf, size)) return -EFAULT; return size; @@ -155,7 +236,7 @@ static struct fuse_req *request_find(struct fuse_conn *fc, unsigned int unique) list_for_each(entry, &fc->processing) { struct fuse_req *tmp; tmp = list_entry(entry, struct fuse_req, list); - if(tmp->in->c.unique == unique) { + if(tmp->unique == unique) { req = tmp; break; } @@ -167,44 +248,55 @@ static struct fuse_req *request_find(struct fuse_conn *fc, unsigned int unique) 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; - struct fuse_out_common oc; char *tmpbuf; + struct fuse_out_header *oh; - if(nbytes > 2 * PAGE_SIZE) { - printk("fuse_dev_write: write is too long\n"); - return -EPROTO; + 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; } - tmpbuf = (char *) __get_free_pages(GFP_KERNEL, 1); + ret = -ENOMEM; + tmpbuf = kmalloc(nbytes, GFP_KERNEL); if(!tmpbuf) - return -ENOMEM; - + goto out; + + ret = -EFAULT; if(copy_from_user(tmpbuf, buf, nbytes)) - return -EFAULT; + goto out_free; 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); + 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); - - return nbytes; + out_free: + kfree(tmpbuf); + out: + return ret; } @@ -213,6 +305,9 @@ 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); @@ -235,8 +330,9 @@ static struct fuse_conn *new_conn(void) init_waitqueue_head(&fc->waitq); INIT_LIST_HEAD(&fc->pending); INIT_LIST_HEAD(&fc->processing); - fc->outstanding = 0; - fc->reqctr = 0; + fc->reqctr = 1; + fc->cleared = NULL; + fc->numcleared = 0; spin_lock(&fuse_lock); fc->id = connctr ++; @@ -265,9 +361,10 @@ static void end_requests(struct list_head *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); + if(req->outsize) + wake_up(&req->waitq); + else + request_free(req); } } @@ -279,6 +376,9 @@ static int fuse_dev_release(struct inode *inode, struct file *file) 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; diff --git a/dir.c b/kernel/dir.c similarity index 58% rename from dir.c rename to kernel/dir.c index 7a3ccc2..92ddc55 100644 --- a/dir.c +++ b/kernel/dir.c @@ -38,36 +38,91 @@ static void init_inode(struct inode *inode, struct fuse_attr *attr) fuse_dir_init(inode); else if(S_ISLNK(inode->i_mode)) fuse_symlink_init(inode); - else + 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_inparam in; - struct fuse_outparam out; + struct fuse_in in = FUSE_IN_INIT; + struct fuse_out out = FUSE_OUT_INIT; + struct fuse_lookup_out arg; struct inode *inode; - in.opcode = FUSE_LOOKUP; - in.ino = dir->i_ino; - strcpy(in.u.lookup.name, entry->d_name.name); - + 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.result) - return ERR_PTR(out.result); + 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, out.u.lookup.ino); + inode = iget(dir->i_sb, arg.ino); if(!inode) return ERR_PTR(-ENOMEM); - init_inode(inode, &out.u.lookup.attr); - + 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) { @@ -78,18 +133,20 @@ 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; + struct fuse_in in = FUSE_IN_INIT; + struct fuse_out out = FUSE_OUT_INIT; + struct fuse_getattr_out arg; - in.opcode = FUSE_GETATTR; - in.ino = inode->i_ino; - + 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.result == 0) - change_attributes(inode, &out.u.getattr.attr); + if(out.h.result == 0) + change_attributes(inode, &arg.attr); - return out.result; + return out.h.result; } @@ -118,7 +175,7 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) while(nbytes >= FUSE_NAME_OFFSET) { struct fuse_dirent *dirent = (struct fuse_dirent *) p; size_t reclen = FUSE_DIRENT_SIZE(dirent); - int err; + int over; if(dirent->namelen > NAME_MAX) { printk("fuse_readdir: name too long\n"); ret = -EPROTO; @@ -127,16 +184,14 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) if(reclen > nbytes) break; - err = filldir(dstbuf, dirent->name, dirent->namelen, + over = filldir(dstbuf, dirent->name, dirent->namelen, file->f_pos, dirent->ino, dirent->type); - if(err) { - ret = err; + if(over) break; - } + p += reclen; file->f_pos += reclen; nbytes -= reclen; - ret ++; } out: @@ -150,34 +205,33 @@ 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; + 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.c.opcode = FUSE_READLINK; - in.c.ino = inode->i_ino; - in.argsize = 0; + in.h.opcode = FUSE_READLINK; + in.h.ino = inode->i_ino; out.arg = (void *) page; - out.argsize = PAGE_SIZE; - + out.argsize = PAGE_SIZE - 1; + out.argvar = 1; request_send(fc, &in, &out); - if(out.c.result) { - __free_page(page); - return out.c.result; + if(out.h.result) { + free_page(page); + return out.h.result; } *bufp = (char *) page; - (*bufp)[PAGE_SIZE - 1] = 0; + (*bufp)[out.argsize] = '\0'; return 0; } static void free_link(char *link) { - __free_page((unsigned long) link); + free_page((unsigned long) link); } static int fuse_readlink(struct dentry *dentry, char *buffer, int buflen) @@ -208,29 +262,31 @@ static int fuse_follow_link(struct dentry *dentry, struct nameidata *nd) return ret; } -static int fuse_open(struct inode *inode, struct file *file) +static int fuse_dir_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; + struct fuse_in in = FUSE_IN_INIT; + struct fuse_out out = FUSE_OUT_INIT; + struct fuse_getdir_out outarg; - request_send(fc, &in, &out); + if(!(file->f_flags & O_DIRECTORY)) + return -EISDIR; - if(out.result == 0) { + 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; - cfile = out.u.open_internal.file; if(!cfile) { - printk("fuse_open: invalid container file\n"); + printk("fuse_getdir: invalid 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"); + printk("fuse_getdir: not a regular file\n"); fput(cfile); return -EPROTO; } @@ -238,28 +294,35 @@ static int fuse_open(struct inode *inode, struct file *file) file->private_data = cfile; } - return out.result; + return out.h.result; } -static int fuse_release(struct inode *inode, struct file *file) +static int fuse_dir_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); + if(!cfile) + BUG(); + + fput(cfile); - return out.result; + 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, }; @@ -267,12 +330,16 @@ static struct inode_operations fuse_dir_inode_operations = static struct file_operations fuse_dir_operations = { read: generic_read_dir, readdir: fuse_readdir, - open: fuse_open, - release: fuse_release, + open: fuse_dir_open, + release: fuse_dir_release, }; -static struct inode_operations fuse_file_inode_operations = -{ +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, }; @@ -304,6 +371,11 @@ 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 diff --git a/fuse_i.h b/kernel/fuse_i.h similarity index 71% rename from fuse_i.h rename to kernel/fuse_i.h index 5f80620..1eb0bb4 100644 --- a/fuse_i.h +++ b/kernel/fuse_i.h @@ -6,14 +6,15 @@ See the file COPYING. */ -#include "fuse.h" - +#include #include #include #include #define FUSE_VERSION "0.1" +#define MAX_CLEARED 256 + /** * A Fuse connection. * @@ -37,8 +38,11 @@ struct fuse_conn { /** The list of requests being processed */ struct list_head processing; - /** The number of outstanding requests */ - int outstanding; + /** The number of cleared inodes */ + unsigned int numcleared; + + /** The array of cleared inode numbers */ + unsigned long *cleared; /** Connnection number (for debuging) */ int id; @@ -54,26 +58,46 @@ struct fuse_req { /** The request list */ struct list_head list; - /** The request input parameters */ - struct fuse_in *in; + /** The request ID */ + int unique; - /** The request result */ - struct fuse_out *out; + /** The opcode */ + enum fuse_opcode opcode; + + /** The request input size */ + unsigned int insize; - /** The file returned by open */ - struct file *file; + /** 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; +}; + - /** True if the request is finished */ - int done; +struct fuse_in { + struct fuse_in_header h; + unsigned int argsize; + const void *arg; }; -struct fuse_out_open_internal { - file *file; +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") @@ -100,6 +124,11 @@ void fuse_file_init(struct inode *inode); */ 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 diff --git a/inode.c b/kernel/inode.c similarity index 68% rename from inode.c rename to kernel/inode.c index 5c2b743..ac2d6be 100644 --- a/inode.c +++ b/kernel/inode.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #define FUSE_SUPER_MAGIC 0x65735546 @@ -20,6 +21,70 @@ 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; @@ -33,6 +98,7 @@ static void fuse_put_super(struct super_block *sb) static struct super_operations fuse_super_operations = { read_inode: fuse_read_inode, + clear_inode: fuse_clear_inode, put_super: fuse_put_super, }; diff --git a/util.c b/kernel/util.c similarity index 100% rename from util.c rename to kernel/util.c 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; +} -- 2.30.2