From: Miklos Szeredi Date: Wed, 31 Oct 2001 14:52:35 +0000 (+0000) Subject: x X-Git-Tag: fuse_0_9~10 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=5e183482b09cec20f54ccb44e767a7fab51833e9;p=qemu-gpiodev%2Flibfuse.git x --- diff --git a/fusepro.c b/fusepro.c index 4f593e7..ff998bf 100644 --- a/fusepro.c +++ b/fusepro.c @@ -1,11 +1,17 @@ -#include +#ifdef linux +/* For pread()/pwrite() */ +#define _XOPEN_SOURCE 500 +#endif +#include #include #include #include #include #include #include +#include +#include static struct fuse *pro_fuse; @@ -53,7 +59,7 @@ static int pro_getdir(const char *path, struct fuse_dh *h, dirfiller_t filler) return res; } -static int pro_mknod(const char *path, int mode, int rdev) +static int pro_mknod(const char *path, mode_t mode, dev_t rdev) { int res; @@ -64,44 +70,44 @@ static int pro_mknod(const char *path, int mode, int rdev) return 0; } -static int pro_symlink(const char *from, const char *to) +static int pro_mkdir(const char *path, mode_t mode) { int res; - res = symlink(from, to); + res = mkdir(path, mode); if(res == -1) return -errno; return 0; } -static int pro_mkdir(const char *path, int mode) +static int pro_unlink(const char *path) { int res; - res = mkdir(path, mode); + res = unlink(path); if(res == -1) return -errno; return 0; } -static int pro_unlink(const char *path) +static int pro_rmdir(const char *path) { int res; - res = unlink(path); + res = rmdir(path); if(res == -1) return -errno; return 0; } -static int pro_rmdir(const char *path) +static int pro_symlink(const char *from, const char *to) { int res; - res = rmdir(path); + res = symlink(from, to); if(res == -1) return -errno; @@ -130,6 +136,80 @@ static int pro_link(const char *from, const char *to) return 0; } +static int pro_chmod(const char *path, mode_t mode) +{ + int res; + + res = chmod(path, mode); + if(res == -1) + return -errno; + + return 0; +} + +static int pro_chown(const char *path, uid_t uid, gid_t gid) +{ + int res; + + res = chown(path, uid, gid); + if(res == -1) + return -errno; + + return 0; +} + +static int pro_truncate(const char *path, off_t size) +{ + int res; + + res = truncate(path, size); + if(res == -1) + return -errno; + + return 0; +} + +static int pro_utime(const char *path, struct utimbuf *buf) +{ + int res; + + res = utime(path, buf); + if(res == -1) + return -errno; + + return 0; +} + + +static int pro_open(const char *path, int flags) +{ + int res; + + res = open(path, flags); + if(res == -1) + return -errno; + + close(res); + return 0; +} + +static int pro_pread(const char *path, char *buf, size_t size, off_t offset) +{ + int fd; + int res; + + fd = open(path, 0); + if(fd == -1) + return -errno; + + res = pread(fd, buf, size, offset); + if(res == -1) + res = -errno; + + close(fd); + return res; +} + static void exit_handler() { exit(0); @@ -176,10 +256,17 @@ static struct fuse_operations pro_oper = { rmdir: pro_rmdir, rename: pro_rename, link: pro_link, + chmod: pro_chmod, + chown: pro_chown, + truncate: pro_truncate, + utime: pro_utime, + open: pro_open, + pread: pro_pread, }; int main(int argc, char *argv[]) { + int res; if(argc != 2) { fprintf(stderr, "usage: %s mount_dir\n", argv[0]); exit(1); @@ -189,7 +276,10 @@ int main(int argc, char *argv[]) atexit(cleanup); pro_fuse = fuse_new(); - fuse_mount(pro_fuse, argv[1]); + res = fuse_mount(pro_fuse, argv[1]); + if(res == -1) + exit(1); + fuse_set_operations(pro_fuse, &pro_oper); fuse_loop(pro_fuse); diff --git a/include/fuse.h b/include/fuse.h index e18f606..bdc96a6 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -10,24 +10,30 @@ #include #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 (*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, int mode, int rdev); - int (*mkdir) (const char *path, int mode); - int (*unlink) (const char *path); - int (*rmdir) (const char *path); - int (*rename) (const char *from, const char *to); - int (*symlink) (const char *from, const char *to); - int (*link) (const char *from, const char *to); + 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); }; struct fuse *fuse_new(); diff --git a/include/linux/fuse.h b/include/linux/fuse.h index 64a4af3..5fe008c 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h @@ -19,11 +19,11 @@ 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 int mode; + unsigned int nlink; + unsigned int uid; + unsigned int gid; + unsigned int rdev; unsigned long long size; unsigned long blksize; unsigned long blocks; @@ -32,19 +32,28 @@ struct fuse_attr { unsigned long ctime; }; +#define FATTR_MODE (1 << 0) +#define FATTR_UID (1 << 1) +#define FATTR_GID (1 << 2) +#define FATTR_SIZE (1 << 3) +#define FATTR_UTIME (1 << 4) + enum fuse_opcode { FUSE_LOOKUP = 1, FUSE_FORGET, FUSE_GETATTR, + FUSE_SETATTR, FUSE_READLINK, + FUSE_SYMLINK, FUSE_GETDIR, FUSE_MKNOD, FUSE_MKDIR, - FUSE_SYMLINK, FUSE_UNLINK, FUSE_RMDIR, FUSE_RENAME, FUSE_LINK, + FUSE_OPEN, + FUSE_READ, }; /* Conservative buffer size for the client */ @@ -90,6 +99,20 @@ struct fuse_link_in { char name[1]; }; +struct fuse_setattr_in { + struct fuse_attr attr; + unsigned int valid; +}; + +struct fuse_open_in { + unsigned int flags; +}; + +struct fuse_read_in { + unsigned long long offset; + unsigned int size; +}; + struct fuse_in_header { int unique; enum fuse_opcode opcode; diff --git a/kernel/Makefile b/kernel/Makefile index f7769d5..4646162 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -5,7 +5,7 @@ CPPFLAGS = -I /lib/modules/`uname -r`/build/include/ -D__KERNEL__ -DMODULE -D_LO all: fuse.o -fuse_objs = dev.o inode.o dir.o util.o +fuse_objs = dev.o inode.o dir.o file.o util.o fuse.o: $(fuse_objs) ld -r -o fuse.o $(fuse_objs) diff --git a/kernel/dev.c b/kernel/dev.c index d32589d..6a8d373 100644 --- a/kernel/dev.c +++ b/kernel/dev.c @@ -87,7 +87,7 @@ static struct fuse_req *request_new(struct fuse_conn *fc, struct fuse_in *inp, { struct fuse_req *req; - req = kmalloc(sizeof(*req), GFP_KERNEL); + req = kmalloc(sizeof(*req), GFP_NOFS); if(!req) return NULL; @@ -98,7 +98,7 @@ static struct fuse_req *request_new(struct fuse_conn *fc, struct fuse_in *inp, req->out = NULL; req->insize = IHSIZE + inp->argsize; - req->in = kmalloc(req->insize, GFP_KERNEL); + req->in = kmalloc(req->insize, GFP_NOFS); if(!req->in) { request_free(req); return NULL; @@ -188,7 +188,7 @@ static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes, loff_t *off) { int ret; - struct fuse_conn *fc = file->private_data; + struct fuse_conn *fc = DEV_FC(file); struct fuse_req *req; char *tmpbuf; unsigned int size; @@ -249,7 +249,7 @@ 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_conn *fc = DEV_FC(file); struct fuse_req *req; char *tmpbuf; struct fuse_out_header *oh; @@ -302,7 +302,7 @@ static ssize_t fuse_dev_write(struct file *file, const char *buf, static unsigned int fuse_dev_poll(struct file *file, poll_table *wait) { - struct fuse_conn *fc = file->private_data; + struct fuse_conn *fc = DEV_FC(file); unsigned int mask = POLLOUT | POLLWRNORM; if(!fc->sb) @@ -370,7 +370,7 @@ static void end_requests(struct list_head *head) static int fuse_dev_release(struct inode *inode, struct file *file) { - struct fuse_conn *fc = file->private_data; + struct fuse_conn *fc = DEV_FC(file); spin_lock(&fuse_lock); fc->file = NULL; diff --git a/kernel/dir.c b/kernel/dir.c index b23def7..1dac0c8 100644 --- a/kernel/dir.c +++ b/kernel/dir.c @@ -10,9 +10,8 @@ #include #include -#include -#include #include +#include static struct inode_operations fuse_dir_inode_operations; static struct inode_operations fuse_file_inode_operations; @@ -20,13 +19,12 @@ static struct inode_operations fuse_symlink_inode_operations; static struct inode_operations fuse_special_inode_operations; static struct file_operations fuse_dir_operations; -static struct file_operations fuse_file_operations; static struct dentry_operations fuse_dentry_opertations; static void change_attributes(struct inode *inode, struct fuse_attr *attr) { - inode->i_mode = attr->mode; + inode->i_mode = (inode->i_mode & S_IFMT) + (attr->mode & 07777); inode->i_nlink = attr->nlink; inode->i_uid = attr->uid; inode->i_gid = attr->gid; @@ -38,29 +36,46 @@ static void change_attributes(struct inode *inode, struct fuse_attr *attr) inode->i_ctime = attr->ctime; } -void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) +static void fuse_init_inode(struct inode *inode, int mode, int rdev) { - change_attributes(inode, attr); - + inode->i_mode = mode & S_IFMT; if(S_ISREG(inode->i_mode)) { inode->i_op = &fuse_file_inode_operations; - inode->i_fop = &fuse_file_operations; + fuse_init_file_inode(inode); } else if(S_ISDIR(inode->i_mode)) { inode->i_op = &fuse_dir_inode_operations; inode->i_fop = &fuse_dir_operations; } - else if(S_ISLNK(inode->i_mode)) + else if(S_ISLNK(inode->i_mode)) { inode->i_op = &fuse_symlink_inode_operations; + } else { inode->i_op = &fuse_special_inode_operations; - init_special_inode(inode, inode->i_mode, attr->rdev); + init_special_inode(inode, inode->i_mode, rdev); } + inode->u.generic_ip = inode; +} + +struct inode *fuse_iget(struct super_block *sb, ino_t ino, + struct fuse_attr *attr) +{ + struct inode *inode; + + inode = iget(sb, ino); + if(inode) { + if(!inode->u.generic_ip) + fuse_init_inode(inode, attr->mode, attr->rdev); + + change_attributes(inode, attr); + } + + return inode; } static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry) { - struct fuse_conn *fc = dir->i_sb->u.generic_sbp; + 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; @@ -76,11 +91,9 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry) inode = NULL; if(!out.h.error) { - inode = iget(dir->i_sb, arg.ino); + inode = fuse_iget(dir->i_sb, arg.ino, &arg.attr); if(!inode) return ERR_PTR(-ENOMEM); - - fuse_init_inode(inode, &arg.attr); } else if(out.h.error != -ENOENT) return ERR_PTR(out.h.error); @@ -94,7 +107,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry) 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_conn *fc = INO_FC(dir); struct fuse_in in = FUSE_IN_INIT; struct fuse_out out = FUSE_OUT_INIT; struct fuse_mknod_in *inarg; @@ -123,11 +136,10 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode, if(out.h.error) return out.h.error; - inode = iget(dir->i_sb, outarg.ino); + inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr); if(!inode) return -ENOMEM; - - fuse_init_inode(inode, &outarg.attr); + d_instantiate(entry, inode); return 0; @@ -141,7 +153,7 @@ static int fuse_create(struct inode *dir, struct dentry *entry, int mode) static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode) { - struct fuse_conn *fc = dir->i_sb->u.generic_sbp; + struct fuse_conn *fc = INO_FC(dir); struct fuse_in in = FUSE_IN_INIT; struct fuse_out out = FUSE_OUT_INIT; struct fuse_mkdir_in *inarg; @@ -168,7 +180,7 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode) static int fuse_symlink(struct inode *dir, struct dentry *entry, const char *link) { - struct fuse_conn *fc = dir->i_sb->u.generic_sbp; + struct fuse_conn *fc = INO_FC(dir); struct fuse_in in = FUSE_IN_INIT; struct fuse_out out = FUSE_OUT_INIT; char *inarg; @@ -195,7 +207,7 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry, static int fuse_remove(struct inode *dir, struct dentry *entry, enum fuse_opcode op) { - struct fuse_conn *fc = dir->i_sb->u.generic_sbp; + struct fuse_conn *fc = INO_FC(dir); struct fuse_in in = FUSE_IN_INIT; struct fuse_out out = FUSE_OUT_INIT; @@ -220,7 +232,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry) static int fuse_rename(struct inode *olddir, struct dentry *oldent, struct inode *newdir, struct dentry *newent) { - struct fuse_conn *fc = olddir->i_sb->u.generic_sbp; + struct fuse_conn *fc = INO_FC(olddir); struct fuse_in in = FUSE_IN_INIT; struct fuse_out out = FUSE_OUT_INIT; struct fuse_rename_in *inarg; @@ -252,7 +264,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, struct dentry *newent) { struct inode *inode = entry->d_inode; - struct fuse_conn *fc = inode->i_sb->u.generic_sbp; + struct fuse_conn *fc = INO_FC(inode); struct fuse_in in = FUSE_IN_INIT; struct fuse_out out = FUSE_OUT_INIT; struct fuse_link_in *inarg; @@ -286,7 +298,7 @@ static int fuse_permission(struct inode *inode, int mask) 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_conn *fc = INO_FC(inode); struct fuse_in in = FUSE_IN_INIT; struct fuse_out out = FUSE_OUT_INIT; struct fuse_getattr_out arg; @@ -357,7 +369,7 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) 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_conn *fc = INO_FC(inode); struct fuse_in in = FUSE_IN_INIT; struct fuse_out out = FUSE_OUT_INIT; unsigned long page; @@ -417,7 +429,7 @@ static int fuse_follow_link(struct dentry *dentry, struct nameidata *nd) static int fuse_dir_open(struct inode *inode, struct file *file) { - struct fuse_conn *fc = inode->i_sb->u.generic_sbp; + struct fuse_conn *fc = INO_FC(inode); struct fuse_in in = FUSE_IN_INIT; struct fuse_out out = FUSE_OUT_INIT; struct fuse_getdir_out outarg; @@ -453,15 +465,56 @@ 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; - - if(!cfile) - BUG(); - fput(cfile); return 0; } +static unsigned int iattr_to_fattr(struct iattr *iattr, + struct fuse_attr *fattr) +{ + unsigned int ivalid = iattr->ia_valid; + unsigned int fvalid = 0; + + memset(fattr, 0, sizeof(*fattr)); + + if(ivalid & ATTR_MODE) + fvalid |= FATTR_MODE, fattr->mode = iattr->ia_mode; + if(ivalid & ATTR_UID) + fvalid |= FATTR_UID, fattr->uid = iattr->ia_uid; + if(ivalid & ATTR_GID) + fvalid |= FATTR_GID, fattr->gid = iattr->ia_gid; + if(ivalid & ATTR_SIZE) + fvalid |= FATTR_SIZE, fattr->size = iattr->ia_size; + /* You can only _set_ these together (they may change by themselves) */ + if((ivalid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME)) { + fvalid |= FATTR_UTIME; + fattr->atime = iattr->ia_atime; + fattr->mtime = iattr->ia_mtime; + } + + return fvalid; +} + +static int fuse_setattr(struct dentry *entry, struct iattr *attr) +{ + 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_setattr_in arg; + + arg.valid = iattr_to_fattr(attr, &arg.attr); + + in.h.opcode = FUSE_SETATTR; + in.h.ino = inode->i_ino; + in.argsize = sizeof(arg); + in.arg = &arg; + request_send(fc, &in, &out); + + return out.h.error; +} + static int fuse_dentry_revalidate(struct dentry *entry, int flags) { if(!entry->d_inode || !(flags & LOOKUP_CONTINUE)) @@ -481,9 +534,7 @@ static struct inode_operations fuse_dir_inode_operations = rmdir: fuse_rmdir, rename: fuse_rename, link: fuse_link, -#if 0 setattr: fuse_setattr, -#endif permission: fuse_permission, revalidate: fuse_revalidate, }; @@ -496,20 +547,20 @@ static struct file_operations fuse_dir_operations = { }; static struct inode_operations fuse_file_inode_operations = { + setattr: fuse_setattr, permission: fuse_permission, revalidate: fuse_revalidate, }; static struct inode_operations fuse_special_inode_operations = { + setattr: fuse_setattr, permission: fuse_permission, revalidate: fuse_revalidate, }; -static struct file_operations fuse_file_operations = { -}; - static struct inode_operations fuse_symlink_inode_operations = { + setattr: fuse_setattr, readlink: fuse_readlink, follow_link: fuse_follow_link, revalidate: fuse_revalidate, diff --git a/kernel/file.c b/kernel/file.c new file mode 100644 index 0000000..df3a863 --- /dev/null +++ b/kernel/file.c @@ -0,0 +1,88 @@ +/* + 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 + + +static int fuse_open(struct inode *inode, struct file *file) +{ + struct fuse_conn *fc = INO_FC(inode); + struct fuse_in in = FUSE_IN_INIT; + struct fuse_out out = FUSE_OUT_INIT; + struct fuse_open_in arg; + + arg.flags = file->f_flags & ~O_EXCL; + in.h.opcode = FUSE_OPEN; + in.h.ino = inode->i_ino; + in.argsize = sizeof(arg); + in.arg = &arg; + request_send(fc, &in, &out); + + return out.h.error; +} + +static int fuse_readpage(struct file *file, struct page *page) +{ + struct inode *inode = page->mapping->host; + struct fuse_conn *fc = INO_FC(inode); + struct fuse_in in = FUSE_IN_INIT; + struct fuse_out out = FUSE_OUT_INIT; + struct fuse_read_in arg; + char *buffer; + + buffer = kmap(page); + + arg.offset = page->index << PAGE_CACHE_SHIFT; + arg.size = PAGE_CACHE_SIZE; + + in.h.opcode = FUSE_READ; + in.h.ino = inode->i_ino; + in.argsize = sizeof(arg); + in.arg = &arg; + out.argsize = PAGE_CACHE_SIZE; + out.argvar = 1; + out.arg = buffer; + + request_send(fc, &in, &out); + if(!out.h.error) { + if(out.argsize < PAGE_CACHE_SIZE) + memset(buffer + out.argsize, 0, + PAGE_CACHE_SIZE - out.argsize); + SetPageUptodate(page); + } + + kunmap(page); + UnlockPage(page); + + return out.h.error; +} + +static struct file_operations fuse_file_operations = { + open: fuse_open, + read: generic_file_read, +}; + +static struct address_space_operations fuse_file_aops = { + readpage: fuse_readpage, +}; + +void fuse_init_file_inode(struct inode *inode) +{ + inode->i_fop = &fuse_file_operations; + inode->i_data.a_ops = &fuse_file_aops; +} + +/* + * Local Variables: + * indent-tabs-mode: t + * c-basic-offset: 8 + * End: + */ diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h index de0a9e6..aaa395d 100644 --- a/kernel/fuse_i.h +++ b/kernel/fuse_i.h @@ -82,6 +82,9 @@ struct fuse_req { }; +#define INO_FC(inode) ((struct fuse_conn *) (inode)->i_sb->u.generic_sbp) +#define DEV_FC(file) ((struct fuse_conn *) (file)->private_data) + struct fuse_in { struct fuse_in_header h; unsigned int argsize; @@ -111,9 +114,16 @@ extern spinlock_t fuse_lock; /** - * Initialize inode + * Get a filled in inode + */ +struct inode *fuse_iget(struct super_block *sb, ino_t ino, + struct fuse_attr *attr); + + +/** + * Initialise operations on regular file */ -void fuse_init_inode(struct inode *inode, struct fuse_attr *attr); +void fuse_init_file_inode(struct inode *inode); /** * Check if the connection can be released, and if yes, then free the diff --git a/kernel/inode.c b/kernel/inode.c index 702ef57..ed4f760 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -39,7 +39,7 @@ static int alloc_cleared(struct fuse_conn *fc) unsigned long *tmp; spin_unlock(&fuse_lock); - tmp = kmalloc(sizeof(unsigned long) * MAX_CLEARED, GFP_KERNEL); + tmp = kmalloc(sizeof(unsigned long) * MAX_CLEARED, GFP_NOFS); spin_lock(&fuse_lock); if(!fc->file || fc->cleared != NULL) @@ -72,7 +72,7 @@ static unsigned long *add_cleared(struct fuse_conn *fc, unsigned long ino) static void fuse_clear_inode(struct inode *inode) { - struct fuse_conn *fc = inode->i_sb->u.generic_sbp; + struct fuse_conn *fc = INO_FC(inode); unsigned long *forget; spin_lock(&fuse_lock); @@ -139,17 +139,11 @@ static struct fuse_conn *get_conn(struct fuse_mount_data *d) static struct inode *get_root_inode(struct super_block *sb) { - struct inode *root; - - root = iget(sb, 1); - if(root) { - struct fuse_attr attr; - memset(&attr, 0, sizeof(attr)); - attr.mode = S_IFDIR; - fuse_init_inode(root, &attr); - } + struct fuse_attr attr; + memset(&attr, 0, sizeof(attr)); - return root; + attr.mode = S_IFDIR; + return fuse_iget(sb, 1, &attr); } static struct super_block *fuse_read_super(struct super_block *sb, diff --git a/lib/fuse.c b/lib/fuse.c index f033e04..cd919ed 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -27,11 +27,12 @@ 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) +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; } @@ -63,22 +64,43 @@ static struct node *lookup_node(struct fuse *f, fino_t parent, return g_hash_table_lookup(f->nametab, &tmp); } -static fino_t find_node(struct fuse *f, fino_t parent, char *name, int create) +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; +} + +static fino_t find_node(struct fuse *f, fino_t parent, char *name, int mode) { struct node *node; + mode &= S_IFMT; node = lookup_node(f, parent, name); - if(node != NULL) - return get_ino(node); + if(node != NULL) { + if(node->mode == mode) + return get_ino(node); - if(!create) - return (fino_t) -1; + unhash_node(f, node); + } - node = new_node(parent, name); + node = new_node(parent, name, mode); g_hash_table_insert(f->nametab, node, node); return get_ino(node); } +static fino_t find_node_dir(struct fuse *f, fino_t parent, char *name) +{ + struct node *node; + + node = lookup_node(f, parent, name); + if(node != NULL) + return get_ino(node); + else + return (fino_t) -1; +} + static char *get_path(fino_t ino) { GString *s; @@ -91,6 +113,10 @@ static char *get_path(fino_t ino) struct node *node; for(; ino != FUSE_ROOT_INO; ino = node->parent) { node = get_node(ino); + if(node->name == NULL) { + g_string_free(s, TRUE); + return NULL; + } g_string_prepend(s, node->name); g_string_prepend_c(s, '/'); } @@ -104,32 +130,42 @@ static char *get_path(fino_t ino) static char *get_path_name(fino_t ino, const char *name) { - char *path = get_path(ino); - char *path2 = g_strconcat(path, "/", name, NULL); + char *path; + char *path2; + + path = get_path(ino); + if(path == NULL) + return NULL; + + path2 = g_strconcat(path, "/", name, NULL); g_free(path); return path2; } -static void remove_node(struct fuse *f, fino_t ino) +static void destroy_node(struct fuse *f, fino_t ino) { struct node *node = get_node(ino); - g_hash_table_remove(f->nametab, node); + unhash_node(f, node); free_node(node); } +static void remove_node(struct fuse *f, fino_t dir, const char *name) +{ + struct node *node = lookup_node(f, dir, name); + assert(node != NULL); + unhash_node(f, node); +} + 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); - assert(node != NULL); - /* The overwritten node is left to dangle until deleted */ if(newnode != NULL) - g_hash_table_remove(f->nametab, newnode); + unhash_node(f, newnode); - /* The renamed node is not freed, since it's pointer is the key */ g_hash_table_remove(f->nametab, node); g_free(node->name); node->name = g_strdup(newname); @@ -159,7 +195,7 @@ static int fill_dir(struct fuse_dh *dh, char *name, int type) size_t reclen; size_t res; - dirent.ino = find_node(dh->fuse, dh->dir, name, 0); + dirent.ino = find_node_dir(dh->fuse, dh->dir, name); dirent.namelen = strlen(name); strncpy(dirent.name, name, sizeof(dirent.name)); dirent.type = type; @@ -213,14 +249,17 @@ static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name) struct stat buf; struct fuse_lookup_out arg; + res = -ENOENT; path = get_path_name(in->ino, name); - res = -ENOSYS; - if(f->op.getattr) - res = f->op.getattr(path, &buf); - g_free(path); + if(path != NULL) { + res = -ENOSYS; + if(f->op.getattr) + res = f->op.getattr(path, &buf); + g_free(path); + } if(res == 0) { convert_stat(&buf, &arg.attr); - arg.ino = find_node(f, in->ino, name, 1); + arg.ino = find_node(f, in->ino, name, arg.attr.mode); } send_reply(f, in, res, &arg, sizeof(arg)); } @@ -230,7 +269,7 @@ 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]); + destroy_node(f, inos[i]); } static void do_getattr(struct fuse *f, struct fuse_in_header *in) @@ -240,30 +279,78 @@ static void do_getattr(struct fuse *f, struct fuse_in_header *in) struct stat buf; struct fuse_getattr_out arg; + res = -ENOENT; path = get_path(in->ino); - res = -ENOSYS; - if(f->op.getattr) - res = f->op.getattr(path, &buf); - g_free(path); + if(path != NULL) { + res = -ENOSYS; + if(f->op.getattr) + res = f->op.getattr(path, &buf); + g_free(path); + } if(res == 0) convert_stat(&buf, &arg.attr); send_reply(f, in, res, &arg, sizeof(arg)); } +static void do_setattr(struct fuse *f, struct fuse_in_header *in, + struct fuse_setattr_in *arg) +{ + int res; + char *path; + int valid = arg->valid; + struct fuse_attr *attr = &arg->attr; + + res = -ENOENT; + path = get_path(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); + } + if(!res && (valid & (FATTR_UID | FATTR_GID))) { + uid_t uid = (valid & FATTR_UID) ? attr->uid : (uid_t) -1; + gid_t gid = (valid & FATTR_GID) ? attr->gid : (gid_t) -1; + + res = -ENOSYS; + if(f->op.chown) + res = f->op.chown(path, uid, gid); + } + if(!res && (valid & FATTR_SIZE)) { + res = -ENOSYS; + if(f->op.truncate) + res = f->op.truncate(path, attr->size); + } + if(!res && (valid & FATTR_UTIME)) { + struct utimbuf buf; + buf.actime = attr->atime; + buf.modtime = attr->mtime; + res = -ENOSYS; + if(f->op.utime) + res = f->op.utime(path, &buf); + } + g_free(path); + } + send_reply(f, in, res, NULL, 0); +} + static void do_readlink(struct fuse *f, struct fuse_in_header *in) { int res; char link[PATH_MAX + 1]; char *path; - path = get_path(in->ino); - res = -ENOSYS; - if(f->op.readlink) - res = f->op.readlink(path, link, sizeof(link)); - g_free(path); - - send_reply(f, in, res, link, res == 0 ? strlen(link) : 0); + res = -ENOENT; + path = get_path(in->ino); + if(path != NULL) { + res = -ENOSYS; + if(f->op.readlink) + res = f->op.readlink(path, link, sizeof(link)); + g_free(path); + } + send_reply(f, in, res, link, !res ? strlen(link) : 0); } static void do_getdir(struct fuse *f, struct fuse_in_header *in) @@ -277,12 +364,14 @@ static void do_getdir(struct fuse *f, struct fuse_in_header *in) dh.fp = tmpfile(); dh.dir = in->ino; + res = -ENOENT; path = get_path(in->ino); - res = -ENOSYS; - if(f->op.getdir) - res = f->op.getdir(path, &dh, (dirfiller_t) fill_dir); - g_free(path); - + if(path != NULL) { + res = -ENOSYS; + if(f->op.getdir) + res = f->op.getdir(path, &dh, (dirfiller_t) fill_dir); + g_free(path); + } fflush(dh.fp); arg.fd = fileno(dh.fp); send_reply(f, in, res, &arg, sizeof(arg)); @@ -297,17 +386,20 @@ static void do_mknod(struct fuse *f, struct fuse_in_header *in, struct fuse_mknod_out outarg; struct stat buf; + res = -ENOENT; path = get_path_name(in->ino, inarg->name); - res = -ENOSYS; - if(f->op.mknod && f->op.getattr) { - res = f->op.mknod(path, inarg->mode, inarg->rdev); - if(res == 0) - res = f->op.getattr(path, &buf); + if(path != NULL) { + res = -ENOSYS; + if(f->op.mknod && f->op.getattr) { + res = f->op.mknod(path, inarg->mode, inarg->rdev); + if(res == 0) + res = f->op.getattr(path, &buf); + } + g_free(path); } - g_free(path); if(res == 0) { convert_stat(&buf, &outarg.attr); - outarg.ino = find_node(f, in->ino, inarg->name, 1); + outarg.ino = find_node(f, in->ino, inarg->name, outarg.attr.mode); } send_reply(f, in, res, &outarg, sizeof(outarg)); @@ -319,11 +411,14 @@ static void do_mkdir(struct fuse *f, struct fuse_in_header *in, int res; char *path; + res = -ENOENT; path = get_path_name(in->ino, inarg->name); - res = -ENOSYS; - if(f->op.mkdir) - res = f->op.mkdir(path, inarg->mode); - g_free(path); + if(path != NULL) { + res = -ENOSYS; + if(f->op.mkdir) + res = f->op.mkdir(path, inarg->mode); + g_free(path); + } send_reply(f, in, res, NULL, 0); } @@ -332,17 +427,22 @@ static void do_remove(struct fuse *f, struct fuse_in_header *in, char *name) int res; char *path; + res = -ENOENT; path = get_path_name(in->ino, name); - res = -ENOSYS; - if(in->opcode == FUSE_UNLINK) { - if(f->op.unlink) - res = f->op.unlink(path); - } - else { - if(f->op.rmdir) - res = f->op.rmdir(path); + if(path != NULL) { + res = -ENOSYS; + if(in->opcode == FUSE_UNLINK) { + if(f->op.unlink) + res = f->op.unlink(path); + } + else { + if(f->op.rmdir) + res = f->op.rmdir(path); + } + g_free(path); } - g_free(path); + if(res == 0) + remove_node(f, in->ino, name); send_reply(f, in, res, NULL, 0); } @@ -352,11 +452,14 @@ static void do_symlink(struct fuse *f, struct fuse_in_header *in, char *name, int res; char *path; + res = -ENOENT; path = get_path_name(in->ino, name); - res = -ENOSYS; - if(f->op.symlink) - res = f->op.symlink(link, path); - g_free(path); + if(path != NULL) { + res = -ENOSYS; + if(f->op.symlink) + res = f->op.symlink(link, path); + g_free(path); + } send_reply(f, in, res, NULL, 0); } @@ -368,31 +471,90 @@ static void do_rename(struct fuse *f, struct fuse_in_header *in, fino_t newdir = inarg->newdir; char *oldname = inarg->names; char *newname = inarg->names + strlen(oldname) + 1; - char *oldpath = get_path_name(olddir, oldname); - char *newpath = get_path_name(newdir, newname); - - res = -ENOSYS; - if(f->op.rename) - res = f->op.rename(oldpath, newpath); - if(res == 0) - rename_node(f, olddir, oldname, newdir, newname); + char *oldpath; + char *newpath; + + res = -ENOENT; + oldpath = get_path_name(olddir, oldname); + if(oldpath != NULL) { + newpath = get_path_name(newdir, newname); + if(newpath != NULL) { + res = -ENOSYS; + if(f->op.rename) + res = f->op.rename(oldpath, newpath); + if(res == 0) + rename_node(f, olddir, oldname, newdir, newname); + g_free(newpath); + } + g_free(oldpath); + } send_reply(f, in, res, NULL, 0); } static void do_link(struct fuse *f, struct fuse_in_header *in, - struct fuse_link_in *inarg) + struct fuse_link_in *arg) { int res; - char *oldpath = get_path(in->ino); - char *newpath = get_path_name(inarg->newdir, inarg->name); + char *oldpath; + char *newpath; + + res = -ENOENT; + oldpath = get_path(in->ino); + if(oldpath != NULL) { + newpath = get_path_name(arg->newdir, arg->name); + if(newpath != NULL) { + res = -ENOSYS; + if(f->op.link) + res = f->op.link(oldpath, newpath); + g_free(newpath); + } + g_free(oldpath); + } + send_reply(f, in, res, NULL, 0); +} - res = -ENOSYS; - if(f->op.link) - res = f->op.link(oldpath, newpath); +static void do_open(struct fuse *f, struct fuse_in_header *in, + struct fuse_open_in *arg) +{ + int res; + char *path; - send_reply(f, in, res, NULL, 0); + res = -ENOENT; + path = get_path(in->ino); + if(path != NULL) { + res = -ENOSYS; + if(f->op.open) + res = f->op.open(path, arg->flags); + g_free(path); + } + send_reply(f, in, res, NULL, 0); } +static void do_read(struct fuse *f, struct fuse_in_header *in, + struct fuse_read_in *arg) +{ + int res; + char *path; + char *buf = g_malloc(arg->size); + size_t size; + + path = get_path(in->ino); + if(path != NULL) { + res = -ENOSYS; + if(f->op.pread) + res = f->op.pread(path, buf, arg->size, arg->offset); + g_free(path); + } + + size = 0; + if(res > 0) { + size = res; + res = 0; + } + + send_reply(f, in, res, buf, size); + g_free(buf); +} void fuse_loop(struct fuse *f) { @@ -435,6 +597,10 @@ void fuse_loop(struct fuse *f) 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; @@ -469,6 +635,14 @@ void fuse_loop(struct fuse *f) 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 */ diff --git a/lib/fuse_i.h b/lib/fuse_i.h index b3b907b..2d030ae 100644 --- a/lib/fuse_i.h +++ b/lib/fuse_i.h @@ -17,6 +17,7 @@ typedef unsigned long fino_t; struct node { char *name; fino_t parent; + int mode; }; struct fuse { diff --git a/lib/mount.c b/lib/mount.c index ace3c65..48d9d45 100644 --- a/lib/mount.c +++ b/lib/mount.c @@ -103,7 +103,6 @@ int fuse_mount(struct fuse *f, const char *dir) if(f->dir != NULL) return 0; - f->dir = g_strdup(dir); f->fd = open(dev, O_RDWR); if(f->fd == -1) { perror(dev); @@ -115,6 +114,7 @@ int fuse_mount(struct fuse *f, const char *dir) return -1; add_mntent(dev, dir, type); + f->dir = g_strdup(dir); return 0; }