From a181e61ca0119b8c3fd2daa4b8d23add2cda3ed0 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 6 Nov 2001 12:03:23 +0000 Subject: [PATCH] bugfixes --- .cvsignore | 1 + Makefile | 15 +- avfsd.c | 367 ++++++++++++++++++++ fusepro.c | 67 ++-- include/fuse.h | 46 +-- include/linux/fuse.h | 17 + kernel/dev.c | 19 +- kernel/dir.c | 79 +++-- kernel/file.c | 87 +++++ kernel/fuse_i.h | 17 +- kernel/inode.c | 72 +--- kernel/redir_hack.c | 787 +++++++++++++++++++++++++++++++++++++++++++ lib/fuse.c | 493 +++++++++++++++++---------- lib/fuse_i.h | 7 +- usermux.c | 252 ++++++++++++++ 15 files changed, 1990 insertions(+), 336 deletions(-) create mode 100644 avfsd.c create mode 100644 kernel/redir_hack.c create mode 100644 usermux.c diff --git a/.cvsignore b/.cvsignore index 26633aa..5743e9b 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1 +1,2 @@ fusepro +avfsd diff --git a/Makefile b/Makefile index 9f6ff4b..97d19d4 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,14 @@ CC = gcc -CFLAGS = -Wall -W -g `glib-config --cflags` -LDFLAGS = `glib-config --libs` -CPPFLAGS = -Iinclude +CFLAGS = -Wall -g `glib-config --cflags` +LDFLAGS = `glib-config --libs` -ldl -L ../avfs/libneon/ +#LIBXML = -lxml +LIBXML = -lxmltok -lxmlparse +LDLIBS = -lneon $(LIBXML) -lpthread +CPPFLAGS = -Iinclude -I ../avfs/include -all: kernel/fuse.o fusepro +all: kernel/fuse.o fusepro avfsd kernel/fuse.o: FORCE make -C kernel fuse.o @@ -15,11 +18,13 @@ lib/libfuse.a: FORCE fusepro: fusepro.o lib/libfuse.a +avfsd: usermux.o avfsd.o ../avfs/lib/avfs.o lib/libfuse.a + clean: make -C kernel clean make -C lib clean rm -f *.o - rm -f fusepro + rm -f fusepro avfsd rm -f *~ FORCE: diff --git a/avfsd.c b/avfsd.c new file mode 100644 index 0000000..c86e831 --- /dev/null +++ b/avfsd.c @@ -0,0 +1,367 @@ +#include +#include + +#include +#include +#include +#include + +static int check_cred(struct fuse_cred *cred) +{ + if(cred->uid != getuid()) + return -EACCES; + else + return 0; +} + +static int avfs_getattr(struct fuse_cred *cred, const char *path, + struct stat *stbuf) +{ + int res; + + res = check_cred(cred); + if(res) + return res; + + res = virt_lstat(path, stbuf); + if(res == -1) + return -errno; + + return 0; +} + +static int avfs_readlink(struct fuse_cred *cred, const char *path, char *buf, + size_t size) +{ + int res; + + res = check_cred(cred); + if(res) + return res; + + res = virt_readlink(path, buf, size - 1); + if(res == -1) + return -errno; + + buf[res] = '\0'; + return 0; +} + + +static int avfs_getdir(struct fuse_cred *cred, const char *path, + fuse_dirh_t h, fuse_dirfil_t filler) +{ + DIR *dp; + struct dirent *de; + int res; + + res = check_cred(cred); + if(res) + return res; + + dp = virt_opendir(path); + if(dp == NULL) + return -errno; + + while((de = virt_readdir(dp)) != NULL) { + res = filler(h, de->d_name, de->d_type); + if(res != 0) + break; + } + + virt_closedir(dp); + return res; +} + +static int avfs_mknod(struct fuse_cred *cred, const char *path, mode_t mode, + dev_t rdev) +{ + int res; + + res = check_cred(cred); + if(res) + return res; + + res = virt_mknod(path, mode, rdev); + if(res == -1) + return -errno; + + return 0; +} + +static int avfs_mkdir(struct fuse_cred *cred, const char *path, mode_t mode) +{ + int res; + + res = check_cred(cred); + if(res) + return res; + + res = virt_mkdir(path, mode); + if(res == -1) + return -errno; + + return 0; +} + +static int avfs_unlink(struct fuse_cred *cred, const char *path) +{ + int res; + + res = check_cred(cred); + if(res) + return res; + + res = virt_unlink(path); + if(res == -1) + return -errno; + + return 0; +} + +static int avfs_rmdir(struct fuse_cred *cred, const char *path) +{ + int res; + + res = check_cred(cred); + if(res) + return res; + + res = virt_rmdir(path); + if(res == -1) + return -errno; + + return 0; +} + +static int avfs_symlink(struct fuse_cred *cred, const char *from, + const char *to) +{ + int res; + + res = check_cred(cred); + if(res) + return res; + + res = virt_symlink(from, to); + if(res == -1) + return -errno; + + return 0; +} + +static int avfs_rename(struct fuse_cred *cred, const char *from, + const char *to) +{ + int res; + + res = check_cred(cred); + if(res) + return res; + + res = virt_rename(from, to); + if(res == -1) + return -errno; + + return 0; +} + +static int avfs_link(struct fuse_cred *cred, const char *from, const char *to) +{ + int res; + + res = check_cred(cred); + if(res) + return res; + + res = virt_link(from, to); + if(res == -1) + return -errno; + + return 0; +} + +static int avfs_chmod(struct fuse_cred *cred, const char *path, mode_t mode) +{ + int res; + + res = check_cred(cred); + if(res) + return res; + + res = virt_chmod(path, mode); + if(res == -1) + return -errno; + + return 0; +} + +static int avfs_chown(struct fuse_cred *cred, const char *path, uid_t uid, + gid_t gid) +{ + int res; + + res = check_cred(cred); + if(res) + return res; + + res = virt_lchown(path, uid, gid); + if(res == -1) + return -errno; + + return 0; +} + +static int avfs_truncate(struct fuse_cred *cred, const char *path, off_t size) +{ + int res; + + res = check_cred(cred); + if(res) + return res; + + res = virt_truncate(path, size); + if(res == -1) + return -errno; + + return 0; +} + +static int avfs_utime(struct fuse_cred *cred, const char *path, + struct utimbuf *buf) +{ + int res; + + res = check_cred(cred); + if(res) + return res; + + res = virt_utime(path, buf); + if(res == -1) + return -errno; + + return 0; +} + + +static int avfs_open(struct fuse_cred *cred, const char *path, int flags) +{ + int res; + + res = check_cred(cred); + if(res) + return res; + + res = virt_open(path, flags, 0); + if(res == -1) + return -errno; + + virt_close(res); + return 0; +} + +static int avfs_read(struct fuse_cred *cred, const char *path, char *buf, + size_t size, off_t offset) +{ + int fd; + int res; + + res = check_cred(cred); + if(res) + return res; + + fd = virt_open(path, O_RDONLY, 0); + if(fd == -1) + return -errno; + + res = virt_lseek(fd, offset, SEEK_SET); + if(res == -1) + res = -errno; + else { + res = virt_read(fd, buf, size); + if(res == -1) + res = -errno; + } + + virt_close(fd); + return res; +} + +static int avfs_write(struct fuse_cred *cred, const char *path, + const char *buf, size_t size, off_t offset) +{ + int fd; + int res; + + res = check_cred(cred); + if(res) + return res; + + fd = virt_open(path, O_WRONLY, 0); + if(fd == -1) + return -errno; + + res = virt_lseek(fd, offset, SEEK_SET); + if(res == -1) + res = -errno; + else { + res = virt_write(fd, buf, size); + if(res == -1) + res = -errno; + } + + virt_close(fd); + return res; +} + +static struct fuse_operations avfs_oper = { + getattr: avfs_getattr, + readlink: avfs_readlink, + getdir: avfs_getdir, + mknod: avfs_mknod, + mkdir: avfs_mkdir, + symlink: avfs_symlink, + unlink: avfs_unlink, + rmdir: avfs_rmdir, + rename: avfs_rename, + link: avfs_link, + chmod: avfs_chmod, + chown: avfs_chown, + truncate: avfs_truncate, + utime: avfs_utime, + open: avfs_open, + read: avfs_read, + write: avfs_write, +}; + + +void avfs_main(struct fuse *fuse) +{ + fuse_set_operations(fuse, &avfs_oper); + fuse_loop(fuse); +} + +#if 0 +int main(int argc, char *argv[]) +{ + int res; + const char *avfs_dir; + struct fuse *fuse; + + if(argc != 2) { + fprintf(stderr, "usage: %s mount_dir\n", argv[0]); + exit(1); + } + + avfs_dir = argv[1]; + + fuse = fuse_new(0); + res = fuse_mount(fuse, avfs_dir); + if(res == -1) + exit(1); + + avfs_main(fuse); + + return 0; +} +#endif diff --git a/fusepro.c b/fusepro.c index ff998bf..ce32000 100644 --- a/fusepro.c +++ b/fusepro.c @@ -15,7 +15,8 @@ static struct fuse *pro_fuse; -static int pro_getattr(const char *path, struct stat *stbuf) +static int pro_getattr(struct fuse_cred *cred, const char *path, + struct stat *stbuf) { int res; @@ -26,7 +27,8 @@ static int pro_getattr(const char *path, struct stat *stbuf) return 0; } -static int pro_readlink(const char *path, char *buf, size_t size) +static int pro_readlink(struct fuse_cred *cred, const char *path, char *buf, + size_t size) { int res; @@ -39,7 +41,8 @@ static int pro_readlink(const char *path, char *buf, size_t size) } -static int pro_getdir(const char *path, struct fuse_dh *h, dirfiller_t filler) +static int pro_getdir(struct fuse_cred *cred, const char *path, fuse_dirh_t h, + fuse_dirfil_t filler) { DIR *dp; struct dirent *de; @@ -59,7 +62,8 @@ static int pro_getdir(const char *path, struct fuse_dh *h, dirfiller_t filler) return res; } -static int pro_mknod(const char *path, mode_t mode, dev_t rdev) +static int pro_mknod(struct fuse_cred *cred, const char *path, mode_t mode, + dev_t rdev) { int res; @@ -70,7 +74,7 @@ static int pro_mknod(const char *path, mode_t mode, dev_t rdev) return 0; } -static int pro_mkdir(const char *path, mode_t mode) +static int pro_mkdir(struct fuse_cred *cred, const char *path, mode_t mode) { int res; @@ -81,7 +85,7 @@ static int pro_mkdir(const char *path, mode_t mode) return 0; } -static int pro_unlink(const char *path) +static int pro_unlink(struct fuse_cred *cred, const char *path) { int res; @@ -92,7 +96,7 @@ static int pro_unlink(const char *path) return 0; } -static int pro_rmdir(const char *path) +static int pro_rmdir(struct fuse_cred *cred, const char *path) { int res; @@ -103,7 +107,8 @@ static int pro_rmdir(const char *path) return 0; } -static int pro_symlink(const char *from, const char *to) +static int pro_symlink(struct fuse_cred *cred, const char *from, + const char *to) { int res; @@ -114,7 +119,7 @@ static int pro_symlink(const char *from, const char *to) return 0; } -static int pro_rename(const char *from, const char *to) +static int pro_rename(struct fuse_cred *cred, const char *from, const char *to) { int res; @@ -125,7 +130,7 @@ static int pro_rename(const char *from, const char *to) return 0; } -static int pro_link(const char *from, const char *to) +static int pro_link(struct fuse_cred *cred, const char *from, const char *to) { int res; @@ -136,7 +141,7 @@ static int pro_link(const char *from, const char *to) return 0; } -static int pro_chmod(const char *path, mode_t mode) +static int pro_chmod(struct fuse_cred *cred, const char *path, mode_t mode) { int res; @@ -147,18 +152,19 @@ static int pro_chmod(const char *path, mode_t mode) return 0; } -static int pro_chown(const char *path, uid_t uid, gid_t gid) +static int pro_chown(struct fuse_cred *cred, const char *path, uid_t uid, + gid_t gid) { int res; - res = chown(path, uid, gid); + res = lchown(path, uid, gid); if(res == -1) return -errno; return 0; } -static int pro_truncate(const char *path, off_t size) +static int pro_truncate(struct fuse_cred *cred, const char *path, off_t size) { int res; @@ -169,7 +175,8 @@ static int pro_truncate(const char *path, off_t size) return 0; } -static int pro_utime(const char *path, struct utimbuf *buf) +static int pro_utime(struct fuse_cred *cred, const char *path, + struct utimbuf *buf) { int res; @@ -181,7 +188,7 @@ static int pro_utime(const char *path, struct utimbuf *buf) } -static int pro_open(const char *path, int flags) +static int pro_open(struct fuse_cred *cred, const char *path, int flags) { int res; @@ -193,12 +200,13 @@ static int pro_open(const char *path, int flags) return 0; } -static int pro_pread(const char *path, char *buf, size_t size, off_t offset) +static int pro_read(struct fuse_cred *cred, const char *path, char *buf, + size_t size, off_t offset) { int fd; int res; - fd = open(path, 0); + fd = open(path, O_RDONLY); if(fd == -1) return -errno; @@ -210,6 +218,24 @@ static int pro_pread(const char *path, char *buf, size_t size, off_t offset) return res; } +static int pro_write(struct fuse_cred *cred, const char *path, const char *buf, + size_t size, off_t offset) +{ + int fd; + int res; + + fd = open(path, O_WRONLY); + if(fd == -1) + return -errno; + + res = pwrite(fd, buf, size, offset); + if(res == -1) + res = -errno; + + close(fd); + return res; +} + static void exit_handler() { exit(0); @@ -261,7 +287,8 @@ static struct fuse_operations pro_oper = { truncate: pro_truncate, utime: pro_utime, open: pro_open, - pread: pro_pread, + read: pro_read, + write: pro_write, }; int main(int argc, char *argv[]) @@ -275,7 +302,7 @@ int main(int argc, char *argv[]) set_signal_handlers(); atexit(cleanup); - pro_fuse = fuse_new(); + pro_fuse = fuse_new(0); res = fuse_mount(pro_fuse, argv[1]); if(res == -1) exit(1); diff --git a/include/fuse.h b/include/fuse.h index bdc96a6..4edb0c7 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -13,30 +13,38 @@ #include struct fuse; -struct fuse_dh; +typedef struct fuse_dirhandle *fuse_dirh_t; -typedef int (*dirfiller_t) (struct fuse_dh *, const char *, int type); +typedef int (*fuse_dirfil_t) (fuse_dirh_t, const char *, int type); + +struct fuse_cred { + uid_t uid; + gid_t gid; +}; struct fuse_operations { - int (*getattr) (const char *path, struct stat *stbuf); - int (*readlink) (const char *path, char *buf, size_t size); - int (*getdir) (const char *path, struct fuse_dh *h, dirfiller_t filler); - int (*mknod) (const char *path, mode_t mode, dev_t rdev); - int (*mkdir) (const char *path, mode_t mode); - int (*unlink) (const char *path); - int (*rmdir) (const char *path); - int (*symlink) (const char *from, const char *to); - int (*rename) (const char *from, const char *to); - int (*link) (const char *from, const char *to); - int (*chmod) (const char *path, mode_t mode); - int (*chown) (const char *path, uid_t uid, gid_t gid); - int (*truncate) (const char *path, off_t size); - int (*utime) (const char *path, struct utimbuf *buf); - int (*open) (const char *path, int flags); - int (*pread) (const char *path, char *buf, size_t size, off_t offset); + int (*getattr) (struct fuse_cred *, const char *, struct stat *); + int (*readlink) (struct fuse_cred *, const char *, char *, size_t); + int (*getdir) (struct fuse_cred *, const char *, fuse_dirh_t, fuse_dirfil_t); + int (*mknod) (struct fuse_cred *, const char *, mode_t, dev_t); + int (*mkdir) (struct fuse_cred *, const char *, mode_t); + int (*unlink) (struct fuse_cred *, const char *); + int (*rmdir) (struct fuse_cred *, const char *); + int (*symlink) (struct fuse_cred *, const char *, const char *); + int (*rename) (struct fuse_cred *, const char *, const char *); + int (*link) (struct fuse_cred *, const char *, const char *); + int (*chmod) (struct fuse_cred *, const char *, mode_t); + int (*chown) (struct fuse_cred *, const char *, uid_t, gid_t); + int (*truncate) (struct fuse_cred *, const char *, off_t); + int (*utime) (struct fuse_cred *, const char *, struct utimbuf *); + int (*open) (struct fuse_cred *, const char *, int); + int (*read) (struct fuse_cred *, const char *, char *, size_t, off_t); + int (*write) (struct fuse_cred *, const char *, const char *, size_t, off_t); }; -struct fuse *fuse_new(); +#define FUSE_MULTITHREAD (1 << 0) + +struct fuse *fuse_new(int flags); int fuse_mount(struct fuse *f, const char *dir); diff --git a/include/linux/fuse.h b/include/linux/fuse.h index 5fe008c..a6e74df 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h @@ -54,6 +54,7 @@ enum fuse_opcode { FUSE_LINK, FUSE_OPEN, FUSE_READ, + FUSE_WRITE, }; /* Conservative buffer size for the client */ @@ -64,6 +65,10 @@ struct fuse_lookup_out { struct fuse_attr attr; }; +struct fuse_forget_in { + int version; +}; + struct fuse_getattr_out { struct fuse_attr attr; }; @@ -104,6 +109,10 @@ struct fuse_setattr_in { unsigned int valid; }; +struct fuse_setattr_out { + unsigned long long newsize; +}; + struct fuse_open_in { unsigned int flags; }; @@ -113,10 +122,18 @@ struct fuse_read_in { unsigned int size; }; +struct fuse_write_in { + unsigned long long offset; + unsigned int size; + char buf[1]; +}; + struct fuse_in_header { int unique; enum fuse_opcode opcode; unsigned long ino; + unsigned int uid; + unsigned int gid; }; struct fuse_out_header { diff --git a/kernel/dev.c b/kernel/dev.c index 6a8d373..8fdb37f 100644 --- a/kernel/dev.c +++ b/kernel/dev.c @@ -128,7 +128,7 @@ void request_send(struct fuse_conn *fc, struct fuse_in *inp, spin_lock(&fuse_lock); ret = -ENOTCONN; - if(fc->file == NULL) + if(!fc->file) goto out_unlock_free; ih = (struct fuse_in_header *) req->in; @@ -204,7 +204,7 @@ static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes, 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); + printk("fuse_dev_read: buffer too small\n"); ret = -EIO; goto err; } @@ -221,6 +221,7 @@ static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes, if(copy_to_user(buf, tmpbuf, size)) return -EFAULT; + kfree(tmpbuf); return size; err: @@ -259,12 +260,12 @@ static ssize_t fuse_dev_write(struct file *file, const char *buf, ret = -EIO; if(nbytes < OHSIZE || nbytes > OHSIZE + PAGE_SIZE) { - printk("fuse_dev_write[%i]: write is short or long\n", fc->id); + printk("fuse_dev_write: write is short or long\n"); goto out; } ret = -ENOMEM; - tmpbuf = kmalloc(nbytes, GFP_KERNEL); + tmpbuf = kmalloc(nbytes, GFP_NOFS); if(!tmpbuf) goto out; @@ -320,7 +321,6 @@ static unsigned int fuse_dev_poll(struct file *file, poll_table *wait) static struct fuse_conn *new_conn(void) { - static int connctr = 1; struct fuse_conn *fc; fc = kmalloc(sizeof(*fc), GFP_KERNEL); @@ -331,12 +331,6 @@ static struct fuse_conn *new_conn(void) 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; } @@ -376,9 +370,6 @@ 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/kernel/dir.c b/kernel/dir.c index 1dac0c8..f506816 100644 --- a/kernel/dir.c +++ b/kernel/dir.c @@ -16,7 +16,6 @@ static struct inode_operations fuse_dir_inode_operations; static struct inode_operations fuse_file_inode_operations; static struct inode_operations fuse_symlink_inode_operations; -static struct inode_operations fuse_special_inode_operations; static struct file_operations fuse_dir_operations; @@ -24,6 +23,9 @@ static struct dentry_operations fuse_dentry_opertations; static void change_attributes(struct inode *inode, struct fuse_attr *attr) { + if(S_ISREG(inode->i_mode) && inode->i_size != attr->size) + invalidate_inode_pages(inode); + inode->i_mode = (inode->i_mode & S_IFMT) + (attr->mode & 07777); inode->i_nlink = attr->nlink; inode->i_uid = attr->uid; @@ -36,9 +38,10 @@ static void change_attributes(struct inode *inode, struct fuse_attr *attr) inode->i_ctime = attr->ctime; } -static void fuse_init_inode(struct inode *inode, int mode, int rdev) +static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) { - inode->i_mode = mode & S_IFMT; + inode->i_mode = attr->mode & S_IFMT; + inode->i_size = attr->size; if(S_ISREG(inode->i_mode)) { inode->i_op = &fuse_file_inode_operations; fuse_init_file_inode(inode); @@ -51,23 +54,24 @@ static void fuse_init_inode(struct inode *inode, int mode, int rdev) inode->i_op = &fuse_symlink_inode_operations; } else { - inode->i_op = &fuse_special_inode_operations; - init_special_inode(inode, inode->i_mode, rdev); + inode->i_op = &fuse_file_inode_operations; + init_special_inode(inode, inode->i_mode, attr->rdev); } inode->u.generic_ip = inode; } struct inode *fuse_iget(struct super_block *sb, ino_t ino, - struct fuse_attr *attr) + struct fuse_attr *attr, int version) { struct inode *inode; inode = iget(sb, ino); if(inode) { if(!inode->u.generic_ip) - fuse_init_inode(inode, attr->mode, attr->rdev); + fuse_init_inode(inode, attr); change_attributes(inode, attr); + inode->i_version = version; } return inode; @@ -75,12 +79,13 @@ struct inode *fuse_iget(struct super_block *sb, ino_t ino, static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry) { + int ret; struct fuse_conn *fc = INO_FC(dir); 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; @@ -91,16 +96,24 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry) inode = NULL; if(!out.h.error) { - inode = fuse_iget(dir->i_sb, arg.ino, &arg.attr); + ret = -ENOMEM; + inode = fuse_iget(dir->i_sb, arg.ino, &arg.attr, out.h.unique); if(!inode) - return ERR_PTR(-ENOMEM); + goto err; + } + else if(out.h.error != -ENOENT) { + ret = out.h.error; + goto err; } - else if(out.h.error != -ENOENT) - return ERR_PTR(out.h.error); + entry->d_time = jiffies; entry->d_op = &fuse_dentry_opertations; d_add(entry, inode); + return NULL; + + err: + return ERR_PTR(ret); } /* create needs to return a positive entry, so this also does a lookup */ @@ -136,12 +149,11 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode, if(out.h.error) return out.h.error; - inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr); + inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr, out.h.unique); if(!inode) return -ENOMEM; d_instantiate(entry, inode); - return 0; } @@ -293,17 +305,16 @@ static int fuse_permission(struct inode *inode, int mask) return 0; } -/* Only revalidate the root inode, since lookup is always redone on - the last path segment, and lookup refreshes the attributes */ -static int fuse_revalidate(struct dentry *dentry) +static int fuse_revalidate(struct dentry *entry) { - struct inode *inode = dentry->d_inode; + struct inode *inode = entry->d_inode; struct fuse_conn *fc = INO_FC(inode); struct fuse_in in = FUSE_IN_INIT; struct fuse_out out = FUSE_OUT_INIT; struct fuse_getattr_out arg; - if(inode->i_ino != FUSE_ROOT_INO) + if(inode->i_ino != FUSE_ROOT_INO && + time_before_eq(jiffies, entry->d_time + HZ / 100)) return 0; in.h.opcode = FUSE_GETATTR; @@ -435,7 +446,7 @@ static int fuse_dir_open(struct inode *inode, struct file *file) struct fuse_getdir_out outarg; if(!(file->f_flags & O_DIRECTORY)) - return -EISDIR; + return 0; in.h.opcode = FUSE_GETDIR; in.h.ino = inode->i_ino; @@ -465,7 +476,9 @@ static int fuse_dir_open(struct inode *inode, struct file *file) static int fuse_dir_release(struct inode *inode, struct file *file) { struct file *cfile = file->private_data; - fput(cfile); + + if(cfile) + fput(cfile); return 0; } @@ -502,16 +515,26 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr) struct fuse_conn *fc = INO_FC(inode); struct fuse_in in = FUSE_IN_INIT; struct fuse_out out = FUSE_OUT_INIT; - struct fuse_setattr_in arg; + struct fuse_setattr_in inarg; + struct fuse_setattr_out outarg; - arg.valid = iattr_to_fattr(attr, &arg.attr); + inarg.valid = iattr_to_fattr(attr, &inarg.attr); in.h.opcode = FUSE_SETATTR; in.h.ino = inode->i_ino; - in.argsize = sizeof(arg); - in.arg = &arg; + in.argsize = sizeof(inarg); + in.arg = &inarg; + out.argsize = sizeof(outarg); + out.arg = &outarg; request_send(fc, &in, &out); + if(!out.h.error && (attr->ia_valid & ATTR_SIZE)) { + if(outarg.newsize > attr->ia_size) + outarg.newsize = attr->ia_size; + + vmtruncate(inode, outarg.newsize); + } + return out.h.error; } @@ -552,12 +575,6 @@ static struct inode_operations fuse_file_inode_operations = { revalidate: fuse_revalidate, }; -static struct inode_operations fuse_special_inode_operations = { - setattr: fuse_setattr, - permission: fuse_permission, - revalidate: fuse_revalidate, -}; - static struct inode_operations fuse_symlink_inode_operations = { setattr: fuse_setattr, diff --git a/kernel/file.c b/kernel/file.c index df3a863..a677d4c 100644 --- a/kernel/file.c +++ b/kernel/file.c @@ -10,6 +10,7 @@ #include #include #include +#include static int fuse_open(struct inode *inode, struct file *file) @@ -25,10 +26,13 @@ static int fuse_open(struct inode *inode, struct file *file) in.argsize = sizeof(arg); in.arg = &arg; request_send(fc, &in, &out); + if(!out.h.error) + invalidate_inode_pages(inode); return out.h.error; } + static int fuse_readpage(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; @@ -65,13 +69,96 @@ static int fuse_readpage(struct file *file, struct page *page) return out.h.error; } +static int write_buffer(struct inode *inode, struct page *page, + unsigned offset, size_t count) +{ + struct fuse_conn *fc = INO_FC(inode); + struct fuse_in in = FUSE_IN_INIT; + struct fuse_out out = FUSE_OUT_INIT; + struct fuse_write_in *arg; + size_t argsize; + char *buffer; + + argsize = offsetof(struct fuse_write_in, buf) + count; + arg = kmalloc(argsize, GFP_KERNEL); + if(!arg) + return -ENOMEM; + + arg->offset = (page->index << PAGE_CACHE_SHIFT) + offset; + arg->size = count; + buffer = kmap(page); + memcpy(arg->buf, buffer + offset, count); + kunmap(page); + + in.h.opcode = FUSE_WRITE; + in.h.ino = inode->i_ino; + in.argsize = argsize; + in.arg = arg; + request_send(fc, &in, &out); + kfree(arg); + + return out.h.error; +} + + +static int fuse_writepage(struct page *page) +{ + struct inode *inode = page->mapping->host; + unsigned count; + unsigned long end_index; + int err; + + end_index = inode->i_size >> PAGE_CACHE_SHIFT; + if(page->index < end_index) + count = PAGE_CACHE_SIZE; + else { + count = inode->i_size & (PAGE_CACHE_SIZE - 1); + err = -EIO; + if(page->index > end_index || count == 0) + goto out; + + } + err = write_buffer(inode, page, 0, count); + out: + UnlockPage(page); + return 0; +} + + +static int fuse_prepare_write(struct file *file, struct page *page, + unsigned offset, unsigned to) +{ + /* No op */ + return 0; +} + +static int fuse_commit_write(struct file *file, struct page *page, + unsigned offset, unsigned to) +{ + int err; + struct inode *inode = page->mapping->host; + + err = write_buffer(inode, page, offset, to - offset); + if(!err) { + loff_t pos = (page->index << PAGE_CACHE_SHIFT) + to; + if(pos > inode->i_size) + inode->i_size = pos; + } + return err; +} + static struct file_operations fuse_file_operations = { open: fuse_open, read: generic_file_read, + write: generic_file_write, + mmap: generic_file_mmap, }; static struct address_space_operations fuse_file_aops = { readpage: fuse_readpage, + writepage: fuse_writepage, + prepare_write: fuse_prepare_write, + commit_write: fuse_commit_write, }; void fuse_init_file_inode(struct inode *inode) diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h index aaa395d..b738bed 100644 --- a/kernel/fuse_i.h +++ b/kernel/fuse_i.h @@ -10,6 +10,7 @@ #include #include #include +#include #define FUSE_VERSION "0.1" @@ -38,15 +39,6 @@ struct fuse_conn { /** 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; }; @@ -70,8 +62,7 @@ struct fuse_req { /** The request input */ char *in; - /** The maximum request output size, if zero, then the request is - asynchronous */ + /** The maximum request output size */ unsigned int outsize; /** The request output */ @@ -98,7 +89,7 @@ struct fuse_out { void *arg; }; -#define FUSE_IN_INIT { {0, 0, 0}, 0, 0 } +#define FUSE_IN_INIT { {0, 0, 0, current->fsuid, current->fsgid}, 0, 0 } #define FUSE_OUT_INIT { {0, 0}, 0, 0, 0 } @@ -117,7 +108,7 @@ extern spinlock_t fuse_lock; * Get a filled in inode */ struct inode *fuse_iget(struct super_block *sb, ino_t ino, - struct fuse_attr *attr); + struct fuse_attr *attr, int version); /** diff --git a/kernel/inode.c b/kernel/inode.c index ed4f760..77e8469 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -21,70 +21,22 @@ static void fuse_read_inode(struct inode *inode) /* No op */ } -static void send_forget(struct fuse_conn *fc, unsigned long *forget, - unsigned int numforget) +static void fuse_clear_inode(struct inode *inode) { + struct fuse_conn *fc = INO_FC(inode); struct fuse_in in = FUSE_IN_INIT; + struct fuse_forget_in arg; + + arg.version = inode->i_version; in.h.opcode = FUSE_FORGET; - in.h.ino = 0; - in.argsize = numforget * sizeof(unsigned long); - in.arg = forget; + in.h.ino = inode->i_ino; + in.argsize = sizeof(arg); + in.arg = &arg; 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_NOFS); - 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 = INO_FC(inode); - 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; @@ -143,7 +95,7 @@ static struct inode *get_root_inode(struct super_block *sb) memset(&attr, 0, sizeof(attr)); attr.mode = S_IFDIR; - return fuse_iget(sb, 1, &attr); + return fuse_iget(sb, 1, &attr, 0); } static struct super_block *fuse_read_super(struct super_block *sb, @@ -169,8 +121,7 @@ static struct super_block *fuse_read_super(struct super_block *sb, goto err; if(fc->sb != NULL) { - printk("fuse_read_super: connection %i already mounted\n", - fc->id); + printk("fuse_read_super: connection already mounted\n"); goto err; } @@ -213,6 +164,5 @@ void fuse_fs_cleanup() * Local Variables: * indent-tabs-mode: t * c-basic-offset: 8 - * End: - */ + * End: */ diff --git a/kernel/redir_hack.c b/kernel/redir_hack.c new file mode 100644 index 0000000..4c91a4f --- /dev/null +++ b/kernel/redir_hack.c @@ -0,0 +1,787 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define DEB(X) printk X +#else +#define DEB(X) +#endif + +#define REDIR_VERSION "0.3" + +extern void *sys_call_table[]; + +typedef asmlinkage int (*chdir_func) (const char *); +typedef asmlinkage int (*stat_func) (const char *, struct stat *); +typedef asmlinkage int (*access_func) (const char *, int); +typedef asmlinkage int (*open_func) (const char *, int, int); +typedef asmlinkage int (*readlink_func) (const char *, char *, int); +typedef asmlinkage int (*getcwd_func) (char *, unsigned long); + +static chdir_func orig_chdir; +static stat_func orig_stat; +static stat_func orig_lstat; +static access_func orig_access; +static open_func orig_open; +static readlink_func orig_readlink; +static getcwd_func orig_getcwd; + +typedef asmlinkage long (*stat64_func) (const char *, struct stat64 *, long); + +static stat64_func orig_stat64; +static stat64_func orig_lstat64; + +#ifdef __i386__ +typedef asmlinkage int (*execve_func) (struct pt_regs); + +static execve_func orig_execve; +#endif + +#define AVFS_MAGIC_CHAR '#' + +#define PF_AVFS 0x00008000 + +#define OVERLAY_BASE "/mnt/tmp" + +#define path_ok(pwd) (pwd->d_parent == pwd || !list_empty(&pwd->d_hash)) + +static char *path_pwd(char *page) +{ + return d_path(current->fs->pwd, current->fs->pwdmnt, page, PAGE_SIZE); +} + +static int a_path_walk(const char *pathname, int flags, struct nameidata *nd) +{ + int error; + + error = 0; + if (path_init(pathname, flags, nd)) + error = path_walk(pathname, nd); + + return error; +} + +static void a_path_release(struct nameidata *nd) +{ + dput(nd->dentry); + mntput(nd->mnt); +} + +static char *resolv_virt(const char *pathname, int must_exist, int flags) +{ + struct nameidata root; + struct nameidata nd; + struct dentry *origroot; + struct vfsmount *origrootmnt; + char *newpathname = NULL; + char *page = NULL; + char *path = NULL; + int pathlen = 0; + int error; + int newflags; + char overlay_dir[128]; + unsigned overlay_dir_len; + + sprintf(overlay_dir, "%s/%u", OVERLAY_BASE, current->fsuid); + overlay_dir_len = strlen(overlay_dir); + + lock_kernel(); + + DEB((KERN_INFO "resolve_virt pathname: '%s'\n", + pathname ? pathname : "(null)")); + + error = a_path_walk(overlay_dir, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &root); + if(error) + goto out; + + origroot = current->fs->root; + origrootmnt = current->fs->rootmnt; + + current->fs->root = root.dentry; + current->fs->rootmnt = root.mnt; + + newflags = flags; + if(must_exist) + newflags |= LOOKUP_POSITIVE; + + error = a_path_walk(pathname, newflags, &nd); + if(!error) { + if(path_ok(nd.dentry)) { + page = (char *) __get_free_page(GFP_USER); + if(page) { + path = d_path(nd.dentry, nd.mnt, page, + PAGE_SIZE); + DEB((KERN_INFO "resolve_virt path = '%s'\n", + path)); + pathlen = (unsigned int) page + PAGE_SIZE - + (unsigned int) path; + } + } + a_path_release(&nd); + } + + current->fs->root = origroot; + current->fs->rootmnt = origrootmnt; + + a_path_release(&root); + + if(path) { + int isvirtual; + + error = a_path_walk(path, flags, &nd); + if(!error) { + if(nd.dentry->d_inode) + isvirtual = 0; + else if(must_exist) + isvirtual = 1; + else if(strchr(path, AVFS_MAGIC_CHAR)) + isvirtual = 1; + else + isvirtual = 0; + + a_path_release(&nd); + } + else { + isvirtual = 1; + } + + if(!isvirtual) { + newpathname = kmalloc(pathlen + 1, GFP_USER); + if(newpathname) + strncpy(newpathname, path, pathlen); + } + else { + newpathname = kmalloc(overlay_dir_len + pathlen + 1, + GFP_USER); + + if(newpathname) { + strcpy(newpathname, overlay_dir); + strncat(newpathname, path, pathlen); + } + } + } + + if(page) + free_page((unsigned long) page); + + + DEB((KERN_INFO "resolve_virt newpathname: '%s'\n", + newpathname ? newpathname : "(null)")); + + out: + unlock_kernel(); + return newpathname; +} + + +#define FUSE_SUPER_MAGIC 0x65735546 + +#define cwd_virtual() \ + (current->fs->pwd->d_sb->s_magic == FUSE_SUPER_MAGIC) + +static char *get_abs_path(const char *filename) +{ + char *cwd; + int cwdlen, fnamelen; + char *abspath, *s; + char *page; + char overlay_dir[128]; + unsigned overlay_dir_len; + + sprintf(overlay_dir, "/mnt/avfs/%010u", current->fsuid); + overlay_dir_len = strlen(overlay_dir); + + if(!path_ok(current->fs->pwd)) + return NULL; + + page = (char *) __get_free_page(GFP_USER); + if(!page) + return NULL; + + cwd = path_pwd(page); + cwdlen = (unsigned int) page + PAGE_SIZE - (unsigned int) cwd - 1; + if(cwd_virtual() && cwdlen > overlay_dir_len) { + cwd += overlay_dir_len; + cwdlen -= overlay_dir_len; + } + + + fnamelen = strlen(filename); + + abspath = kmalloc(cwdlen + 1 + fnamelen + 1, GFP_USER); + if(abspath) { + s = abspath; + strncpy(s, cwd, cwdlen); + s += cwdlen; + *s++ = '/'; + strncpy(s, filename, fnamelen + 1); + } + free_page((unsigned long) page); + + return abspath; +} + +static char *resolve_name(const char *kfilename, int must_exist, int flags) +{ + char *tmp; + char *newfilename; + + tmp = getname(kfilename); + if(IS_ERR(tmp)) + return tmp; + + + if((tmp[0] != '/' && cwd_virtual()) || strchr(tmp, AVFS_MAGIC_CHAR)) { + DEB((KERN_INFO "resolve_name: %s (%i/%s)\n", tmp, + current->pid, + (current->flags & PF_AVFS) ? "on" : "off")); + + if(strcmp(tmp, "/#avfs-on") == 0) { + printk(KERN_INFO "AVFS ON (pid: %i)\n", + current->pid); + current->flags |= PF_AVFS; + newfilename = ERR_PTR(-EEXIST); + } + else if(!(current->flags & PF_AVFS)) + newfilename = NULL; + else if(strcmp(tmp, "/#avfs-off") == 0) { + printk(KERN_INFO "AVFS OFF (pid: %i)\n", + current->pid); + current->flags &= ~PF_AVFS; + newfilename = ERR_PTR(-EEXIST); + } + else { + if(tmp[0] == '/') { + newfilename = resolv_virt(tmp, must_exist, flags); + } + else { + char *abspath; + + abspath = get_abs_path(tmp); + if(abspath) { + newfilename = resolv_virt(abspath, must_exist, flags); + kfree(abspath); + } + else + newfilename = NULL; + } + } + } + else + newfilename = NULL; + + putname(tmp); + + return newfilename; +} + +asmlinkage int virt_chdir(const char *filename) +{ + int ret; + mm_segment_t old_fs; + char *newfilename; + + if(!cwd_virtual()) { + ret = (*orig_chdir)(filename); + if(ret != -ENOENT) + return ret; + } + else + ret = 0; + + newfilename = resolve_name(filename, 1, 1); + if(!newfilename) { + if(ret) + return ret; + else + return (*orig_chdir)(filename); + } + if(IS_ERR(newfilename)) + return PTR_ERR(newfilename); + + + DEB((KERN_INFO "CHDIR: trying '%s'\n", newfilename)); + + old_fs = get_fs(); + set_fs(get_ds()); + ret = (*orig_chdir)(newfilename); + set_fs(old_fs); + kfree(newfilename); + + DEB((KERN_INFO "CHDIR: result %i\n", ret)); + + return ret; +} + +static int do_orig_stat(stat_func sfunc, const char *filename, + struct stat *statbuf) +{ + int ret; + mm_segment_t old_fs; + struct stat locbuf; + + old_fs = get_fs(); + set_fs(get_ds()); + ret = (*sfunc)(filename, &locbuf); + set_fs(old_fs); + + if(ret == 0) + ret = (copy_to_user(statbuf, &locbuf, sizeof(locbuf)) ? + -EFAULT : 0); + + return ret; +} + +asmlinkage int virt_stat(const char *filename, struct stat *statbuf) +{ + int ret; + char *newfilename; + + if(!cwd_virtual()) { + ret = (*orig_stat)(filename, statbuf); + if(ret != -ENOENT) + return ret; + } + else + ret = 0; + + newfilename = resolve_name(filename, 1, 1); + if(!newfilename) { + if(ret) + return ret; + else + return (*orig_stat)(filename, statbuf); + } + if(IS_ERR(newfilename)) + return PTR_ERR(newfilename); + + DEB((KERN_INFO "STAT: trying '%s'\n", newfilename)); + + ret = do_orig_stat(orig_stat, newfilename, statbuf); + kfree(newfilename); + + DEB((KERN_INFO "STAT: result %i\n", ret)); + + return ret; +} + +asmlinkage int virt_lstat(const char *filename, struct stat *statbuf) +{ + int ret; + char *newfilename; + + if(!cwd_virtual()) { + ret = (*orig_lstat)(filename, statbuf); + if(ret != -ENOENT) + return ret; + } + else + ret = 0; + + newfilename = resolve_name(filename, 1, 0); + if(!newfilename) { + if(ret) + return ret; + else + return (*orig_lstat)(filename, statbuf); + } + if(IS_ERR(newfilename)) + return PTR_ERR(newfilename); + + DEB((KERN_INFO "LSTAT: trying '%s'\n", newfilename)); + + ret = do_orig_stat(orig_lstat, newfilename, statbuf); + kfree(newfilename); + + DEB((KERN_INFO "LSTAT: result %i\n", ret)); + + return ret; +} + + +asmlinkage int virt_access(const char *filename, int mode) +{ + int ret; + mm_segment_t old_fs; + char *newfilename; + + if(!cwd_virtual()) { + ret = (*orig_access)(filename, mode); + if(ret != -ENOENT) + return ret; + } + else + ret = 0; + + newfilename = resolve_name(filename, 1, 1); + if(!newfilename) { + if(ret) + return ret; + else + return (*orig_access)(filename, mode); + } + if(IS_ERR(newfilename)) + return PTR_ERR(newfilename); + + DEB((KERN_INFO "ACCESS: trying '%s'\n", newfilename)); + + old_fs = get_fs(); + set_fs(get_ds()); + ret = (*orig_access)(newfilename, mode); + set_fs(old_fs); + kfree(newfilename); + + DEB((KERN_INFO "ACCESS: result %i\n", ret)); + + return ret; +} + +asmlinkage int virt_open(const char *filename, int flags, int mode) +{ + int ret; + mm_segment_t old_fs; + char *newfilename; + + if(!cwd_virtual()) { + ret = (*orig_open)(filename, flags, mode); + if(ret != -ENOENT) + return ret; + } + else + ret = 0; + + newfilename = resolve_name(filename, 1, 1); + if(!newfilename) { + if(ret) + return ret; + else + return (*orig_open)(filename, flags, mode); + } + if(IS_ERR(newfilename)) + return PTR_ERR(newfilename); + + DEB((KERN_INFO "OPEN: trying '%s'\n", newfilename)); + + old_fs = get_fs(); + set_fs(get_ds()); + ret = (*orig_open)(newfilename, flags, mode); + set_fs(old_fs); + kfree(newfilename); + + DEB((KERN_INFO "OPEN: result %i\n", ret)); + + return ret; +} + +asmlinkage int virt_readlink(const char *filename, char *buf, int bufsiz) +{ + int ret; + mm_segment_t old_fs; + char *newfilename; + char *locbuf; + int len; + + if(!cwd_virtual()) { + ret = (*orig_readlink)(filename, buf, bufsiz); + if(ret != -ENOENT) + return ret; + } + else + ret = 0; + + newfilename = resolve_name(filename, 1, 0); + if(!newfilename) { + if(ret) + return ret; + else + return (*orig_readlink)(filename, buf, bufsiz); + } + if(IS_ERR(newfilename)) + return PTR_ERR(newfilename); + + DEB((KERN_INFO "READLINK: trying '%s'\n", newfilename)); + + /* bufsiz is legal (already checked by sys_readlink) */ + len = bufsiz; + if(bufsiz > PAGE_SIZE) + len = PAGE_SIZE; + + locbuf = (char *) __get_free_page(GFP_USER); + + ret = -ENOMEM; + if(locbuf) { + old_fs = get_fs(); + set_fs(get_ds()); + ret = (*orig_readlink)(newfilename, locbuf, len); + set_fs(old_fs); + + if(ret >= 0) + if(copy_to_user(buf, locbuf, len)) + ret = -EFAULT; + free_page((unsigned long) locbuf); + } + kfree(newfilename); + + DEB((KERN_INFO "READLINK: result %i\n", ret)); + + return ret; +} + +asmlinkage int virt_getcwd(char *buf, unsigned long size) +{ + int ret; + char *cwd; + unsigned long cwdlen; + char *page; + char *newcwd; + unsigned long newlen; + char overlay_dir[128]; + unsigned overlay_dir_len; + + ret = (*orig_getcwd)(buf, size); + + if(!cwd_virtual() || ret < 0) + return ret; + + if(!path_ok(current->fs->pwd)) + return -ENOENT; + + page = (char *) __get_free_page(GFP_USER); + if(!page) + return -ENOMEM; + + cwd = path_pwd(page); + cwdlen = PAGE_SIZE + (page - cwd) - 1; + + sprintf(overlay_dir, "/mnt/avfs/%010u", current->fsuid); + overlay_dir_len = strlen(overlay_dir); + + if(cwdlen >= overlay_dir_len && + strncmp(cwd, overlay_dir, overlay_dir_len) == 0) { + if(cwdlen == overlay_dir_len) { + newcwd = "/"; + newlen = 1; + } + else { + newcwd = cwd + overlay_dir_len; + newlen = cwdlen - overlay_dir_len; + } + + ret = -ERANGE; + if(newlen + 1 <= size) { + ret = newlen + 1; + if(copy_to_user(buf, newcwd, newlen + 1)) + ret = -EFAULT; + } + } + free_page((unsigned long) page); + + return ret; +} + + +static long do_orig_stat64(stat64_func sfunc, const char *filename, + struct stat64 * statbuf, long flags) +{ + long ret; + mm_segment_t old_fs; + struct stat64 locbuf; + + old_fs = get_fs(); + set_fs(get_ds()); + ret = (*sfunc)(filename, &locbuf, flags); + set_fs(old_fs); + + if(ret == 0) + ret = (copy_to_user(statbuf, &locbuf, sizeof(locbuf)) ? + -EFAULT : 0); + + return ret; +} + +asmlinkage long virt_stat64(char * filename, struct stat64 * statbuf, long flags) +{ + long ret; + char *newfilename; + + if(!cwd_virtual()) { + ret = (*orig_stat64)(filename, statbuf, flags); + if(ret != -ENOENT) + return ret; + } + else + ret = 0; + + newfilename = resolve_name(filename, 1, 1); + if(!newfilename) { + if(ret) + return ret; + else + return (*orig_stat64)(filename, statbuf, flags); + } + if(IS_ERR(newfilename)) + return PTR_ERR(newfilename); + + DEB((KERN_INFO "STAT64: trying '%s'\n", newfilename)); + + ret = do_orig_stat64(orig_stat64, newfilename, statbuf, flags); + kfree(newfilename); + + DEB((KERN_INFO "STAT64: result %li\n", ret)); + + return ret; +} + +asmlinkage long virt_lstat64(char * filename, struct stat64 * statbuf, long flags) +{ + long ret; + char *newfilename; + + if(!cwd_virtual()) { + ret = (*orig_lstat64)(filename, statbuf, flags); + if(ret != -ENOENT) + return ret; + } + else + ret = 0; + + newfilename = resolve_name(filename, 1, 0); + if(!newfilename) { + if(ret) + return ret; + else + return (*orig_lstat64)(filename, statbuf, flags); + } + if(IS_ERR(newfilename)) + return PTR_ERR(newfilename); + + DEB((KERN_INFO "LSTAT64: trying '%s'\n", newfilename)); + + ret = do_orig_stat64(orig_lstat64, newfilename, statbuf, flags); + kfree(newfilename); + + DEB((KERN_INFO "LSTAT64: result %li\n", ret)); + + return ret; +} + +#ifdef __i386__ + +asmlinkage int real_execve(struct pt_regs *regs) +{ + int error; + char * filename; + + filename = getname((char *) regs->ebx); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + error = do_execve(filename, (char **) regs->ecx, (char **) regs->edx, regs); + if (error == 0) + current->ptrace &= ~PT_DTRACE; + putname(filename); + +out: + return error; +} + +asmlinkage int virt_execve(struct pt_regs regs) +{ + int ret; + char *newfilename; + char *filename = (char *) regs.ebx; + + if(!cwd_virtual()) { + ret = real_execve(®s); + if(ret != -ENOENT) + return ret; + } + else + ret = 0; + + newfilename = resolve_name(filename, 1, 1); + if(!newfilename) { + if(ret) + return ret; + else + return real_execve(®s); + } + if(IS_ERR(newfilename)) + return PTR_ERR(newfilename); + + DEB((KERN_INFO "EXECVE: trying '%s'\n", newfilename)); + + ret = do_execve(newfilename, (char **) regs.ecx, (char **) regs.edx, + ®s); + if (ret == 0) + current->ptrace &= ~PT_DTRACE; + kfree(newfilename); + + DEB((KERN_INFO "EXECVE: result %i\n", ret)); + + return ret; +} +#endif /* __i386__ */ + +void *replace_syscall(int index, void *new_syscall) +{ + void *orig_syscall = sys_call_table[index]; + + printk(KERN_INFO "replacing syscall nr. %3i [%p] with [%p]\n", + index, orig_syscall, new_syscall); + sys_call_table[index] = new_syscall; + + return orig_syscall; +} + +int init_module(void) +{ + printk(KERN_INFO "redir init (version %s)\n", REDIR_VERSION); + + orig_chdir = replace_syscall(__NR_chdir, virt_chdir); + orig_stat = replace_syscall(__NR_stat, virt_stat); + orig_lstat = replace_syscall(__NR_lstat, virt_lstat); + orig_access = replace_syscall(__NR_access, virt_access); + orig_open = replace_syscall(__NR_open, virt_open); + orig_readlink = replace_syscall(__NR_readlink, virt_readlink); + orig_getcwd = replace_syscall(__NR_getcwd, virt_getcwd); + + orig_stat64 = replace_syscall(__NR_stat64, virt_stat64); + orig_lstat64 = replace_syscall(__NR_lstat64, virt_lstat64); + +#ifdef __i386__ + orig_execve = replace_syscall(__NR_execve, virt_execve); +#endif + + return 0; +} + + +void cleanup_module(void) +{ + printk(KERN_INFO "redir cleanup\n"); + + replace_syscall(__NR_chdir, orig_chdir); + replace_syscall(__NR_stat, orig_stat); + replace_syscall(__NR_lstat, orig_lstat); + replace_syscall(__NR_access, orig_access); + replace_syscall(__NR_open, orig_open); + replace_syscall(__NR_readlink, orig_readlink); + replace_syscall(__NR_getcwd, orig_getcwd); + + replace_syscall(__NR_stat64, orig_stat64); + replace_syscall(__NR_lstat64, orig_lstat64); + +#ifdef __i386__ + replace_syscall(__NR_execve, orig_execve); +#endif + +} diff --git a/lib/fuse.c b/lib/fuse.c index cd919ed..137b5ef 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -9,12 +9,23 @@ #include "fuse_i.h" #include + #include #include #include #include +static inline struct node *get_node(fino_t ino) +{ + return (struct node *) ((ino << 3) + 0x8000000); +} + +static inline fino_t get_ino(struct node *node) +{ + return (((fino_t) node) - 0x8000000) >> 3; +} + static guint name_hash(const struct node *node) { return g_str_hash(node->name) ^ node->parent; @@ -27,15 +38,6 @@ static gint name_compare(const struct node *node1, const struct node *node2) strcmp(node1->name, node2->name) == 0; } -static struct node *new_node(fino_t parent, const char *name, int mode) -{ - struct node *node = g_new0(struct node, 1); - node->name = g_strdup(name); - node->parent = parent; - node->mode = mode; - return node; -} - static int free_node(struct node *node) { g_free(node->name); @@ -43,16 +45,6 @@ static int free_node(struct node *node) return 1; } -static inline struct node *get_node(fino_t ino) -{ - return (struct node *) ((ino << 3) + 0x8000000); -} - -static inline fino_t get_ino(struct node *node) -{ - return (((fino_t) node) - 0x8000000) >> 3; -} - static struct node *lookup_node(struct fuse *f, fino_t parent, const char *name) { @@ -64,29 +56,51 @@ static struct node *lookup_node(struct fuse *f, fino_t parent, return g_hash_table_lookup(f->nametab, &tmp); } +static void hash_node(struct fuse *f, struct node *node, fino_t parent, + const char *name) +{ + node->name = g_strdup(name); + node->parent = parent; + g_hash_table_insert(f->nametab, node, node); +} + static void unhash_node(struct fuse *f, struct node *node) { - g_hash_table_remove(f->nametab, node); - g_free(node->name); - node->parent = 0; - node->name = NULL; + if(node->name != NULL) { + g_hash_table_remove(f->nametab, node); + g_free(node->name); + node->parent = 0; + node->name = NULL; + } } -static fino_t find_node(struct fuse *f, fino_t parent, char *name, int mode) +static fino_t find_node(struct fuse *f, fino_t parent, char *name, + struct fuse_attr *attr, int version) { struct node *node; - mode &= S_IFMT; + int mode = attr->mode & S_IFMT; + int rdev = 0; + + if(S_ISCHR(mode) || S_ISBLK(mode)) + rdev = attr->rdev; + pthread_mutex_lock(&f->lock); node = lookup_node(f, parent, name); if(node != NULL) { - if(node->mode == mode) - return get_ino(node); - + if(node->mode == mode && node->rdev == rdev) + goto out; + unhash_node(f, node); } - node = new_node(parent, name, mode); - g_hash_table_insert(f->nametab, node, node); + node = g_new0(struct node, 1); + node->mode = mode; + node->rdev = rdev; + hash_node(f, node, parent, name); + + out: + node->version = version; + pthread_mutex_unlock(&f->lock); return get_ino(node); } @@ -94,14 +108,17 @@ static fino_t find_node_dir(struct fuse *f, fino_t parent, char *name) { struct node *node; + pthread_mutex_lock(&f->lock); node = lookup_node(f, parent, name); + pthread_mutex_unlock(&f->lock); + if(node != NULL) return get_ino(node); else return (fino_t) -1; } -static char *get_path(fino_t ino) +static char *get_path(struct fuse *f, fino_t ino) { GString *s; char *ss; @@ -111,15 +128,18 @@ static char *get_path(fino_t ino) g_string_prepend_c(s, '/'); else { struct node *node; + pthread_mutex_lock(&f->lock); for(; ino != FUSE_ROOT_INO; ino = node->parent) { node = get_node(ino); if(node->name == NULL) { + pthread_mutex_unlock(&f->lock); g_string_free(s, TRUE); return NULL; } g_string_prepend(s, node->name); g_string_prepend_c(s, '/'); } + pthread_mutex_unlock(&f->lock); } ss = s->str; @@ -128,49 +148,66 @@ static char *get_path(fino_t ino) return ss; } -static char *get_path_name(fino_t ino, const char *name) +static char *get_path_name(struct fuse *f, fino_t ino, const char *name) { - char *path; char *path2; + char *path; - path = get_path(ino); + if(ino == FUSE_ROOT_INO) + return g_strconcat("/", name, NULL); + + path = get_path(f, ino); if(path == NULL) return NULL; - + path2 = g_strconcat(path, "/", name, NULL); g_free(path); + return path2; } -static void destroy_node(struct fuse *f, fino_t ino) +static void destroy_node(struct fuse *f, fino_t ino, int version) { - struct node *node = get_node(ino); - unhash_node(f, node); - free_node(node); + struct node *node; + + pthread_mutex_lock(&f->lock); + node = get_node(ino); + if(node->version == version) { + unhash_node(f, node); + free_node(node); + } + pthread_mutex_unlock(&f->lock); + } static void remove_node(struct fuse *f, fino_t dir, const char *name) { - struct node *node = lookup_node(f, dir, name); + struct node *node; + + pthread_mutex_lock(&f->lock); + node = lookup_node(f, dir, name); assert(node != NULL); unhash_node(f, node); + pthread_mutex_unlock(&f->lock); } static void rename_node(struct fuse *f, fino_t olddir, const char *oldname, fino_t newdir, const char *newname) { - struct node *node = lookup_node(f, olddir, oldname); - struct node *newnode = lookup_node(f, newdir, newname); + struct node *node; + struct node *newnode; + + pthread_mutex_lock(&f->lock); + node = lookup_node(f, olddir, oldname); + newnode = lookup_node(f, newdir, newname); assert(node != NULL); if(newnode != NULL) unhash_node(f, newnode); - g_hash_table_remove(f->nametab, node); - g_free(node->name); - node->name = g_strdup(newname); - node->parent = newdir; - g_hash_table_insert(f->nametab, node, node); + unhash_node(f, node); + hash_node(f, node, newdir, newname); + pthread_mutex_unlock(&f->lock); } @@ -189,7 +226,7 @@ static void convert_stat(struct stat *stbuf, struct fuse_attr *attr) attr->ctime = stbuf->st_ctime; } -static int fill_dir(struct fuse_dh *dh, char *name, int type) +static int fill_dir(struct fuse_dirhandle *dh, char *name, int type) { struct fuse_dirent dirent; size_t reclen; @@ -234,6 +271,7 @@ static void send_reply(struct fuse *f, struct fuse_in_header *in, int error, printf(" unique: %i, error: %i (%s), outsize: %i\n", out->unique, out->error, strerror(-out->error), outsize); + fflush(stdout); res = write(f->fd, outbuf, outsize); if(res == -1) @@ -242,34 +280,40 @@ static void send_reply(struct fuse *f, struct fuse_in_header *in, int error, g_free(outbuf); } +static void fill_cred(struct fuse_in_header *in, struct fuse_cred *cred) +{ + cred->uid = in->uid; + cred->gid = in->gid; +} + static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name) { int res; char *path; struct stat buf; struct fuse_lookup_out arg; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path_name(in->ino, name); + path = get_path_name(f, in->ino, name); if(path != NULL) { res = -ENOSYS; if(f->op.getattr) - res = f->op.getattr(path, &buf); + res = f->op.getattr(&cred, path, &buf); g_free(path); } if(res == 0) { convert_stat(&buf, &arg.attr); - arg.ino = find_node(f, in->ino, name, arg.attr.mode); + arg.ino = find_node(f, in->ino, name, &arg.attr, in->unique); } send_reply(f, in, res, &arg, sizeof(arg)); } -static void do_forget(struct fuse *f, unsigned long *inos, size_t num) +static void do_forget(struct fuse *f, struct fuse_in_header *in, + struct fuse_forget_in *arg) { - size_t i; - - for(i = 0; i < num; i++) - destroy_node(f, inos[i]); + destroy_node(f, in->ino, arg->version); } static void do_getattr(struct fuse *f, struct fuse_in_header *in) @@ -278,13 +322,15 @@ static void do_getattr(struct fuse *f, struct fuse_in_header *in) char *path; struct stat buf; struct fuse_getattr_out arg; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path(in->ino); + path = get_path(f, in->ino); if(path != NULL) { res = -ENOSYS; if(f->op.getattr) - res = f->op.getattr(path, &buf); + res = f->op.getattr(&cred, path, &buf); g_free(path); } if(res == 0) @@ -300,15 +346,18 @@ static void do_setattr(struct fuse *f, struct fuse_in_header *in, char *path; int valid = arg->valid; struct fuse_attr *attr = &arg->attr; + struct fuse_setattr_out outarg; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path(in->ino); + path = get_path(f, in->ino); if(path != NULL) { res = 0; if(!res && (valid & FATTR_MODE)) { res = -ENOSYS; if(f->op.chmod) - res = f->op.chmod(path, attr->mode); + res = f->op.chmod(&cred, path, attr->mode); } if(!res && (valid & (FATTR_UID | FATTR_GID))) { uid_t uid = (valid & FATTR_UID) ? attr->uid : (uid_t) -1; @@ -316,12 +365,18 @@ static void do_setattr(struct fuse *f, struct fuse_in_header *in, res = -ENOSYS; if(f->op.chown) - res = f->op.chown(path, uid, gid); + res = f->op.chown(&cred, path, uid, gid); } if(!res && (valid & FATTR_SIZE)) { res = -ENOSYS; - if(f->op.truncate) - res = f->op.truncate(path, attr->size); + if(f->op.truncate && f->op.getattr) { + res = f->op.truncate(&cred, path, attr->size); + if(!res) { + struct stat buf; + res = f->op.getattr(&cred, path, &buf); + outarg.newsize = buf.st_size; + } + } } if(!res && (valid & FATTR_UTIME)) { struct utimbuf buf; @@ -329,11 +384,11 @@ static void do_setattr(struct fuse *f, struct fuse_in_header *in, buf.modtime = attr->mtime; res = -ENOSYS; if(f->op.utime) - res = f->op.utime(path, &buf); + res = f->op.utime(&cred, path, &buf); } g_free(path); } - send_reply(f, in, res, NULL, 0); + send_reply(f, in, res, &outarg, sizeof(outarg)); } static void do_readlink(struct fuse *f, struct fuse_in_header *in) @@ -341,13 +396,15 @@ static void do_readlink(struct fuse *f, struct fuse_in_header *in) int res; char link[PATH_MAX + 1]; char *path; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path(in->ino); + path = get_path(f, in->ino); if(path != NULL) { res = -ENOSYS; if(f->op.readlink) - res = f->op.readlink(path, link, sizeof(link)); + res = f->op.readlink(&cred, path, link, sizeof(link)); g_free(path); } send_reply(f, in, res, link, !res ? strlen(link) : 0); @@ -357,19 +414,20 @@ static void do_getdir(struct fuse *f, struct fuse_in_header *in) { int res; struct fuse_getdir_out arg; - struct fuse_dh dh; + struct fuse_dirhandle dh; char *path; + struct fuse_cred cred; + fill_cred(in, &cred); dh.fuse = f; dh.fp = tmpfile(); dh.dir = in->ino; - res = -ENOENT; - path = get_path(in->ino); + path = get_path(f, in->ino); if(path != NULL) { res = -ENOSYS; if(f->op.getdir) - res = f->op.getdir(path, &dh, (dirfiller_t) fill_dir); + res = f->op.getdir(&cred, path, &dh, (fuse_dirfil_t) fill_dir); g_free(path); } fflush(dh.fp); @@ -385,21 +443,24 @@ static void do_mknod(struct fuse *f, struct fuse_in_header *in, char *path; struct fuse_mknod_out outarg; struct stat buf; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path_name(in->ino, inarg->name); + path = get_path_name(f, in->ino, inarg->name); if(path != NULL) { res = -ENOSYS; if(f->op.mknod && f->op.getattr) { - res = f->op.mknod(path, inarg->mode, inarg->rdev); + res = f->op.mknod(&cred, path, inarg->mode, inarg->rdev); if(res == 0) - res = f->op.getattr(path, &buf); + res = f->op.getattr(&cred, path, &buf); } g_free(path); } if(res == 0) { convert_stat(&buf, &outarg.attr); - outarg.ino = find_node(f, in->ino, inarg->name, outarg.attr.mode); + outarg.ino = find_node(f, in->ino, inarg->name, &outarg.attr, + in->unique); } send_reply(f, in, res, &outarg, sizeof(outarg)); @@ -410,13 +471,15 @@ static void do_mkdir(struct fuse *f, struct fuse_in_header *in, { int res; char *path; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path_name(in->ino, inarg->name); + path = get_path_name(f, in->ino, inarg->name); if(path != NULL) { res = -ENOSYS; if(f->op.mkdir) - res = f->op.mkdir(path, inarg->mode); + res = f->op.mkdir(&cred, path, inarg->mode); g_free(path); } send_reply(f, in, res, NULL, 0); @@ -426,18 +489,20 @@ static void do_remove(struct fuse *f, struct fuse_in_header *in, char *name) { int res; char *path; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path_name(in->ino, name); + path = get_path_name(f, in->ino, name); if(path != NULL) { res = -ENOSYS; if(in->opcode == FUSE_UNLINK) { if(f->op.unlink) - res = f->op.unlink(path); + res = f->op.unlink(&cred, path); } else { if(f->op.rmdir) - res = f->op.rmdir(path); + res = f->op.rmdir(&cred, path); } g_free(path); } @@ -451,13 +516,15 @@ static void do_symlink(struct fuse *f, struct fuse_in_header *in, char *name, { int res; char *path; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path_name(in->ino, name); + path = get_path_name(f, in->ino, name); if(path != NULL) { res = -ENOSYS; if(f->op.symlink) - res = f->op.symlink(link, path); + res = f->op.symlink(&cred, link, path); g_free(path); } send_reply(f, in, res, NULL, 0); @@ -473,15 +540,17 @@ static void do_rename(struct fuse *f, struct fuse_in_header *in, char *newname = inarg->names + strlen(oldname) + 1; char *oldpath; char *newpath; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - oldpath = get_path_name(olddir, oldname); + oldpath = get_path_name(f, olddir, oldname); if(oldpath != NULL) { - newpath = get_path_name(newdir, newname); + newpath = get_path_name(f, newdir, newname); if(newpath != NULL) { res = -ENOSYS; if(f->op.rename) - res = f->op.rename(oldpath, newpath); + res = f->op.rename(&cred, oldpath, newpath); if(res == 0) rename_node(f, olddir, oldname, newdir, newname); g_free(newpath); @@ -497,15 +566,17 @@ static void do_link(struct fuse *f, struct fuse_in_header *in, int res; char *oldpath; char *newpath; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - oldpath = get_path(in->ino); + oldpath = get_path(f, in->ino); if(oldpath != NULL) { - newpath = get_path_name(arg->newdir, arg->name); + newpath = get_path_name(f, arg->newdir, arg->name); if(newpath != NULL) { res = -ENOSYS; if(f->op.link) - res = f->op.link(oldpath, newpath); + res = f->op.link(&cred, oldpath, newpath); g_free(newpath); } g_free(oldpath); @@ -518,13 +589,15 @@ static void do_open(struct fuse *f, struct fuse_in_header *in, { int res; char *path; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path(in->ino); + path = get_path(f, in->ino); if(path != NULL) { res = -ENOSYS; if(f->op.open) - res = f->op.open(path, arg->flags); + res = f->op.open(&cred, path, arg->flags); g_free(path); } send_reply(f, in, res, NULL, 0); @@ -537,12 +610,14 @@ static void do_read(struct fuse *f, struct fuse_in_header *in, char *path; char *buf = g_malloc(arg->size); size_t size; + struct fuse_cred cred; - path = get_path(in->ino); + fill_cred(in, &cred); + path = get_path(f, in->ino); if(path != NULL) { res = -ENOSYS; - if(f->op.pread) - res = f->op.pread(path, buf, arg->size, arg->offset); + if(f->op.read) + res = f->op.read(&cred, path, buf, arg->size, arg->offset); g_free(path); } @@ -556,110 +631,183 @@ static void do_read(struct fuse *f, struct fuse_in_header *in, g_free(buf); } +static void do_write(struct fuse *f, struct fuse_in_header *in, + struct fuse_write_in *arg) +{ + int res; + char *path; + struct fuse_cred cred; + + fill_cred(in, &cred); + path = get_path(f, in->ino); + if(path != NULL) { + res = -ENOSYS; + if(f->op.write) + res = f->op.write(&cred, path, arg->buf, arg->size, arg->offset); + g_free(path); + } + + if(res > 0) { + if((size_t) res != arg->size) { + fprintf(stderr, "short write: %u (should be %u)\n", res, + arg->size); + res = -EIO; + } + else + res = 0; + } + + send_reply(f, in, res, NULL, 0); +} + +struct cmd { + struct fuse *f; + char *buf; + size_t buflen; +}; + +static void *do_command(void *data) +{ + struct cmd *cmd = (struct cmd *) data; + struct fuse_in_header *in = (struct fuse_in_header *) cmd->buf; + void *inarg = cmd->buf + sizeof(struct fuse_in_header); + size_t argsize; + struct fuse *f = cmd->f; + + printf("unique: %i, opcode: %i, ino: %li, insize: %i\n", in->unique, + in->opcode, in->ino, cmd->buflen); + fflush(stdout); + + argsize = cmd->buflen - sizeof(struct fuse_in_header); + + switch(in->opcode) { + case FUSE_LOOKUP: + do_lookup(f, in, (char *) inarg); + break; + + case FUSE_FORGET: + do_forget(f, in, (struct fuse_forget_in *) inarg); + break; + + case FUSE_GETATTR: + do_getattr(f, in); + break; + + case FUSE_SETATTR: + do_setattr(f, in, (struct fuse_setattr_in *) inarg); + 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; + + case FUSE_MKDIR: + do_mkdir(f, in, (struct fuse_mkdir_in *) inarg); + break; + + case FUSE_UNLINK: + case FUSE_RMDIR: + do_remove(f, in, (char *) inarg); + break; + + case FUSE_SYMLINK: + do_symlink(f, in, (char *) inarg, + ((char *) inarg) + strlen((char *) inarg) + 1); + break; + + case FUSE_RENAME: + do_rename(f, in, (struct fuse_rename_in *) inarg); + break; + + case FUSE_LINK: + do_link(f, in, (struct fuse_link_in *) inarg); + break; + + case FUSE_OPEN: + do_open(f, in, (struct fuse_open_in *) inarg); + break; + + case FUSE_READ: + do_read(f, in, (struct fuse_read_in *) inarg); + break; + + case FUSE_WRITE: + do_write(f, in, (struct fuse_write_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); + } + + g_free(cmd->buf); + g_free(cmd); + + return NULL; +} + 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; + pthread_attr_t attr; + pthread_t thrid; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); while(1) { + struct cmd *cmd; + res = read(f->fd, inbuf, sizeof(inbuf)); if(res == -1) { perror("reading fuse device"); continue; } - insize = res; - - if(insize < sizeof(struct fuse_in_header)) { + if((size_t) res < 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_SETATTR: - do_setattr(f, in, (struct fuse_setattr_in *) inarg); - 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; - - case FUSE_MKDIR: - do_mkdir(f, in, (struct fuse_mkdir_in *) inarg); - break; - - case FUSE_UNLINK: - case FUSE_RMDIR: - do_remove(f, in, (char *) inarg); - break; - - case FUSE_SYMLINK: - do_symlink(f, in, (char *) inarg, - ((char *) inarg) + strlen((char *) inarg) + 1); - break; - - case FUSE_RENAME: - do_rename(f, in, (struct fuse_rename_in *) inarg); - break; - - case FUSE_LINK: - do_link(f, in, (struct fuse_link_in *) inarg); - break; - - case FUSE_OPEN: - do_open(f, in, (struct fuse_open_in *) inarg); - break; - - case FUSE_READ: - do_read(f, in, (struct fuse_read_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); + cmd = g_new0(struct cmd, 1); + cmd->f = f; + cmd->buflen = res; + cmd->buf = g_malloc(cmd->buflen); + memcpy(cmd->buf, inbuf, cmd->buflen); + + if(f->flags & FUSE_MULTITHREAD) { + res = pthread_create(&thrid, &attr, do_command, cmd); + if(res != 0) { + fprintf(stderr, "Error creating thread: %s\n", + strerror(errno)); + exit(1); + } } + else + do_command(cmd); } } -struct fuse *fuse_new() +struct fuse *fuse_new(int flags) { struct fuse *f = g_new0(struct fuse, 1); + f->flags = flags; f->fd = -1; f->dir = NULL; f->nametab = g_hash_table_new((GHashFunc) name_hash, (GCompareFunc) name_compare); + pthread_mutex_init(&f->lock, NULL); return f; } @@ -671,8 +819,9 @@ void fuse_set_operations(struct fuse *f, const struct fuse_operations *op) void fuse_destroy(struct fuse *f) { - fuse_unmount(f); + close(f->fd); g_hash_table_foreach_remove(f->nametab, (GHRFunc) free_node, NULL); g_hash_table_destroy(f->nametab); + pthread_mutex_destroy(&f->lock); g_free(f); } diff --git a/lib/fuse_i.h b/lib/fuse_i.h index 2d030ae..c28e2e8 100644 --- a/lib/fuse_i.h +++ b/lib/fuse_i.h @@ -9,6 +9,7 @@ #include "fuse.h" #include #include +#include #define FUSE_DEV "/proc/fs/fuse/dev" @@ -18,16 +19,20 @@ struct node { char *name; fino_t parent; int mode; + int rdev; + int version; }; struct fuse { + int flags; char *dir; int fd; struct fuse_operations op; GHashTable *nametab; + pthread_mutex_t lock; }; -struct fuse_dh { +struct fuse_dirhandle { struct fuse *fuse; fino_t dir; FILE *fp; diff --git a/usermux.c b/usermux.c new file mode 100644 index 0000000..4caacb9 --- /dev/null +++ b/usermux.c @@ -0,0 +1,252 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MOUNTDIR "/mnt/avfs" + +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + +static struct fuse *um_fuse; +static const char *um_dir; + +#define MAX_USERS 100 +static uid_t users[MAX_USERS]; +static size_t numusers = 0; + +void avfs_main(struct fuse *fuse); + +static void reset_signal_handlers() +{ + struct sigaction sa; + + sa.sa_handler = SIG_DFL; + sigemptyset(&(sa.sa_mask)); + sa.sa_flags = 0; + + sigaction(SIGCHLD, &sa, NULL); +} + + +static void start_avfs(uid_t uid) +{ + int res; + char *userdir; + struct fuse *user_fuse; + struct passwd pw_buf; + struct passwd *pw; + char buf[1024]; + + res = getpwuid_r(uid, &pw_buf, buf, sizeof(buf), &pw); + if(pw == NULL) + return; + + user_fuse = fuse_new(0); + + userdir = g_strdup_printf("%s/%010u", MOUNTDIR, uid); + mkdir(userdir, 0755); + chown(userdir, pw->pw_uid, pw->pw_gid); + res = fuse_mount(user_fuse, userdir); + g_free(userdir); + + if(res == -1) + return; + + res = fork(); + if(res == 0) { + reset_signal_handlers(); + + initgroups(pw->pw_name, pw->pw_gid); + setgid(pw->pw_gid); + setuid(pw->pw_uid); + + avfs_main(user_fuse); + exit(0); + } + + fuse_destroy(user_fuse); +} + + +static int find_user(const char *userstr, uid_t *uid) +{ + size_t i; + char *end; + + *uid = strtol(userstr, &end, 10); + if(*end != '\0') + return 0; + + pthread_mutex_lock(&lock); + for(i = 0; i < numusers; i++) { + if(users[i] == *uid) { + pthread_mutex_unlock(&lock); + return 1; + } + } + if(numusers == MAX_USERS) { + memmove(users, users + sizeof(users[0]), + (MAX_USERS - 1) * sizeof(users[0])); + numusers --; + } + + users[numusers++] = *uid; + pthread_mutex_unlock(&lock); + + start_avfs(*uid); + + return 1; +} + +static void root_attr(struct stat *stbuf) +{ + stbuf->st_mode = S_IFDIR | 0555; + stbuf->st_nlink = 2 + numusers; + stbuf->st_size = MAX_USERS; + stbuf->st_blksize = 1024; +} + +static int um_getattr(struct fuse_cred *cred, const char *path, + struct stat *stbuf) +{ + uid_t uid; + memset(stbuf, 0, sizeof(*stbuf)); + + if(strcmp(path, "/") == 0) { + root_attr(stbuf); + return 0; + } + + if(!find_user(path+1, &uid)) + return -ENOENT; + + stbuf->st_mode = S_IFLNK | 0777; + stbuf->st_nlink = 1; + stbuf->st_size = strlen(MOUNTDIR) + 1 + 10; + stbuf->st_blksize = 1024; + stbuf->st_uid = uid; + + return 0; +} + +static int um_readlink(struct fuse_cred *cred, const char *path, char *buf, + size_t size) +{ + uid_t uid; + + if(!find_user(path+1, &uid)) + return -ENOENT; + + snprintf(buf, size, "%s/%010u", MOUNTDIR, uid); + return 0; +} + +static int um_getdir(struct fuse_cred *cred, const char *path, fuse_dirh_t h, + fuse_dirfil_t filler) +{ + size_t i; + + if(strcmp(path, "/") != 0) + return 0; + + filler(h, ".", 0); + filler(h, "..", 0); + + pthread_mutex_lock(&lock); + for(i = 0; i < numusers; i++) { + char buf[32]; + + sprintf(buf, "%u", users[i]); + filler(h, buf, 0); + } + pthread_mutex_unlock(&lock); + + return 0; +} + + +static void exit_handler() +{ + exit(0); +} + +static void child_handler() +{ + int status; + wait(&status); +} + +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); + } + + sa.sa_handler = child_handler; + if(sigaction(SIGCHLD, &sa, NULL) == -1) { + perror("Cannot set child signal handler"); + exit(1); + } +} + +static void cleanup() +{ + fuse_unmount(um_fuse); + fuse_destroy(um_fuse); +} + +static struct fuse_operations um_oper = { + getattr: um_getattr, + getdir: um_getdir, + readlink: um_readlink, +}; + +int main(int argc, char *argv[]) +{ + int res; + if(argc != 2) { + fprintf(stderr, "usage: %s mount_dir\n", argv[0]); + exit(1); + } + + um_dir = argv[1]; + + set_signal_handlers(); + atexit(cleanup); + + um_fuse = fuse_new(0); + res = fuse_mount(um_fuse, um_dir); + if(res == -1) + exit(1); + + fuse_set_operations(um_fuse, &um_oper); + fuse_loop(um_fuse); + + return 0; +} -- 2.30.2