From: Miklos Szeredi Date: Thu, 19 Feb 2004 16:55:40 +0000 (+0000) Subject: improvements to the kernel interface X-Git-Tag: fuse_1_9~88 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=76f6578928809ac2e687dc0979f3051a00b56970;p=qemu-gpiodev%2Flibfuse.git improvements to the kernel interface --- diff --git a/ChangeLog b/ChangeLog index 54cfd16..cdc2579 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,7 +3,18 @@ * statfs library API changed to match other methods. Since this is not backward compatible FUSE_MAJOR_VERSION is changed to 2 - * statfs kernel interface changed to 64 bits, added 'bavail' field + * kernel interface changes follow: + + * statfs changed to 64 bits, added 'bavail' field + + * add generation number to lookup result + + * optimized mknod/mkdir/symlink/link (no separate lookup is + needed) + + * rdev size increased to 32 bits for mknod + + * kernel interface version changed to 3.1 2004-02-18 Miklos Szeredi diff --git a/include/linux/fuse.h b/include/linux/fuse.h index 2b516a5..9bca31c 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h @@ -117,6 +117,7 @@ enum fuse_opcode { struct fuse_lookup_out { unsigned long ino; + unsigned long generation; struct fuse_attr attr; }; @@ -133,19 +134,13 @@ struct fuse_getdir_out { void *file; /* Used by kernel only */ }; -/* FIXME: 2.6 needs 32 bit rdev */ struct fuse_mknod_in { - unsigned short mode; - unsigned short rdev; -}; - -struct fuse_mknod_out { - unsigned long ino; - struct fuse_attr attr; + unsigned int mode; + unsigned int rdev; }; struct fuse_mkdir_in { - unsigned short mode; + unsigned int mode; }; struct fuse_rename_in { diff --git a/kernel/dir.c b/kernel/dir.c index 5fec669..b9c26e6 100644 --- a/kernel/dir.c +++ b/kernel/dir.c @@ -74,23 +74,29 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) else if(S_ISLNK(inode->i_mode)) { inode->i_op = &fuse_symlink_inode_operations; } - else { + else if(S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || + S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)){ inode->i_op = &fuse_file_inode_operations; init_special_inode(inode, inode->i_mode, new_decode_dev(attr->rdev)); - } + } else + printk("fuse_init_inode: bad file type: %o\n", inode->i_mode); + inode->u.generic_ip = inode; } -struct inode *fuse_iget(struct super_block *sb, ino_t ino, +struct inode *fuse_iget(struct super_block *sb, ino_t ino, int generation, struct fuse_attr *attr, int version) { struct inode *inode; inode = iget(sb, ino); if(inode) { - if(!inode->u.generic_ip) + if(!inode->u.generic_ip) { + inode->i_generation = generation; fuse_init_inode(inode, attr); + } else if(inode->i_generation != generation) + printk("fuse_iget: bad generation for ino %lu\n", ino); change_attributes(inode, attr); inode->i_version = version; @@ -130,7 +136,8 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, err = fuse_do_lookup(dir, entry, &outarg, &version); if(!err) { - inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr, version); + inode = fuse_iget(dir->i_sb, outarg.ino, outarg.generation, + &outarg.attr, version); if(!inode) return -ENOMEM; } else if(err != -ENOENT) @@ -142,8 +149,28 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, return 0; } -/* create needs to return a positive entry, so this is actually an - mknod+lookup */ +static int lookup_new_entry(struct inode *dir, struct dentry *entry, + struct fuse_lookup_out *outarg, int version, + int mode) +{ + struct inode *inode; + inode = fuse_iget(dir->i_sb, outarg->ino, outarg->generation, + &outarg->attr, version); + if(!inode) + return -ENOMEM; + + /* Don't allow userspace to do really stupid things... */ + if((inode->i_mode ^ mode) & S_IFMT) { + iput(inode); + printk("fuse_mknod: inode has wrong type\n"); + return -EINVAL; + } + + d_instantiate(entry, inode); + return 0; +} + + static int _fuse_mknod(struct inode *dir, struct dentry *entry, int mode, dev_t rdev) { @@ -151,8 +178,7 @@ static int _fuse_mknod(struct inode *dir, struct dentry *entry, int mode, struct fuse_in in = FUSE_IN_INIT; struct fuse_out out = FUSE_OUT_INIT; struct fuse_mknod_in inarg; - struct fuse_mknod_out outarg; - struct inode *inode; + struct fuse_lookup_out outarg; memset(&inarg, 0, sizeof(inarg)); inarg.mode = mode; @@ -173,19 +199,7 @@ 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, out.h.unique); - if(!inode) - return -ENOMEM; - - /* Don't allow userspace to do really stupid things... */ - if((inode->i_mode ^ mode) & S_IFMT) { - iput(inode); - printk("fuse_mknod: inode has wrong type\n"); - return -EPROTO; - } - - d_instantiate(entry, inode); - return 0; + return lookup_new_entry(dir, entry, &outarg, out.h.unique, mode); } static int _fuse_create(struct inode *dir, struct dentry *entry, int mode) @@ -193,20 +207,6 @@ static int _fuse_create(struct inode *dir, struct dentry *entry, int mode) return _fuse_mknod(dir, entry, mode, 0); } -/* knfsd needs the new entry instantiated in mkdir/symlink/link. this - should rather be done like mknod: attributes returned in out arg to - save a call to userspace */ -static int lookup_new_entry(struct inode *dir, struct dentry *entry) -{ - struct inode *inode; - int err = fuse_lookup_iget(dir, entry, &inode); - if(err || !inode) { - printk("fuse_mkdir: failed to look up new entry\n"); - return err ? err : -ENOENT; - } - d_instantiate(entry, inode); - return 0; -} static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode) { @@ -214,6 +214,7 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode) struct fuse_in in = FUSE_IN_INIT; struct fuse_out out = FUSE_OUT_INIT; struct fuse_mkdir_in inarg; + struct fuse_lookup_out outarg; memset(&inarg, 0, sizeof(inarg)); inarg.mode = mode; @@ -225,11 +226,14 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode) in.args[0].value = &inarg; in.args[1].size = entry->d_name.len + 1; in.args[1].value = entry->d_name.name; + out.numargs = 1; + out.args[0].size = sizeof(outarg); + out.args[0].value = &outarg; request_send(fc, &in, &out); if(out.h.error) return out.h.error; - return lookup_new_entry(dir, entry); + return lookup_new_entry(dir, entry, &outarg, out.h.unique, S_IFDIR); } static int fuse_symlink(struct inode *dir, struct dentry *entry, @@ -238,6 +242,7 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry, 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 outarg; in.h.opcode = FUSE_SYMLINK; in.h.ino = dir->i_ino; @@ -246,11 +251,14 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry, in.args[0].value = entry->d_name.name; in.args[1].size = strlen(link) + 1; in.args[1].value = link; + out.numargs = 1; + out.args[0].size = sizeof(outarg); + out.args[0].value = &outarg; request_send(fc, &in, &out); if(out.h.error) return out.h.error; - return lookup_new_entry(dir, entry); + return lookup_new_entry(dir, entry, &outarg, out.h.unique, S_IFLNK); } static int fuse_remove(struct inode *dir, struct dentry *entry, @@ -325,6 +333,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, struct fuse_in in = FUSE_IN_INIT; struct fuse_out out = FUSE_OUT_INIT; struct fuse_link_in inarg; + struct fuse_lookup_out outarg; memset(&inarg, 0, sizeof(inarg)); inarg.newdir = newdir->i_ino; @@ -336,13 +345,17 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, in.args[0].value = &inarg; in.args[1].size = newent->d_name.len + 1; in.args[1].value = newent->d_name.name; + out.numargs = 1; + out.args[0].size = sizeof(outarg); + out.args[0].value = &outarg; request_send(fc, &in, &out); if(out.h.error) return out.h.error; /* Invalidate old entry, so attributes are refreshed */ d_invalidate(entry); - return lookup_new_entry(newdir, newent); + return lookup_new_entry(newdir, newent, &outarg, out.h.unique, + inode->i_mode); } int fuse_do_getattr(struct inode *inode) diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h index d4a677e..48e52c9 100644 --- a/kernel/fuse_i.h +++ b/kernel/fuse_i.h @@ -174,7 +174,7 @@ extern spinlock_t fuse_lock; /** * Get a filled in inode */ -struct inode *fuse_iget(struct super_block *sb, ino_t ino, +struct inode *fuse_iget(struct super_block *sb, ino_t ino, int generation, struct fuse_attr *attr, int version); diff --git a/kernel/inode.c b/kernel/inode.c index 9ab0dc3..08bf3c9 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -148,7 +148,7 @@ static struct inode *get_root_inode(struct super_block *sb, unsigned int mode) memset(&attr, 0, sizeof(attr)); attr.mode = mode; - return fuse_iget(sb, 1, &attr, 0); + return fuse_iget(sb, 1, 0, &attr, 0); } @@ -158,7 +158,7 @@ static struct dentry *fuse_get_dentry(struct super_block *sb, void *vobjp) { __u32 *objp = vobjp; unsigned long ino = objp[0]; - /* __u32 generation = objp[1]; */ + __u32 generation = objp[1]; struct inode *inode; struct dentry *entry; @@ -166,7 +166,7 @@ static struct dentry *fuse_get_dentry(struct super_block *sb, void *vobjp) return ERR_PTR(-ESTALE); inode = ilookup(sb, ino); - if(!inode) + if(!inode || inode->i_generation != generation) return ERR_PTR(-ESTALE); entry = d_alloc_anon(inode); diff --git a/lib/fuse.c b/lib/fuse.c index ffb3489..079220a 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -81,11 +81,9 @@ static struct node *get_node(struct fuse *f, fino_t ino) abort(); } -static void hash_ino(struct fuse *f, struct node *node, fino_t ino) +static void hash_ino(struct fuse *f, struct node *node) { - size_t hash = ino % f->ino_table_size; - node->ino = ino; - + size_t hash = node->ino % f->ino_table_size; node->ino_next = f->ino_table[hash]; f->ino_table[hash] = node; } @@ -102,16 +100,13 @@ static void unhash_ino(struct fuse *f, struct node *node) } } -static fino_t get_ino(struct node *node) -{ - return node->ino; -} - static fino_t next_ino(struct fuse *f) { - do f->ctr++; - while(f->ctr == 0 || __get_node(f, f->ctr) != NULL); - + do { + f->ctr++; + if(!f->ctr) + f->generation ++; + } while(f->ctr == 0 || __get_node(f, f->ctr) != NULL); return f->ctr; } @@ -176,8 +171,8 @@ static void unhash_name(struct fuse *f, struct node *node) } } -static fino_t find_node(struct fuse *f, fino_t parent, char *name, - struct fuse_attr *attr, int version) +static struct node *find_node(struct fuse *f, fino_t parent, char *name, + struct fuse_attr *attr, int version) { struct node *node; int mode = attr->mode & S_IFMT; @@ -198,13 +193,15 @@ static fino_t find_node(struct fuse *f, fino_t parent, char *name, node = (struct node *) calloc(1, sizeof(struct node)); node->mode = mode; node->rdev = rdev; - hash_ino(f, node, next_ino(f)); + node->ino = next_ino(f); + node->generation = f->generation; + hash_ino(f, node); hash_name(f, node, parent, name); out: node->version = version; pthread_mutex_unlock(&f->lock); - return get_ino(node); + return node; } static char *add_name(char *buf, char *s, const char *name) @@ -407,11 +404,33 @@ static int send_reply(struct fuse *f, struct fuse_in_header *in, int error, return res; } +static int lookup_path(struct fuse *f, fino_t ino, int version, char *name, + const char *path, struct fuse_lookup_out *arg) +{ + int res; + struct stat buf; + + res = f->op.getattr(path, &buf); + if(res == 0) { + struct node *node; + + memset(arg, 0, sizeof(struct fuse_lookup_out)); + convert_stat(&buf, &arg->attr); + node = find_node(f, ino, name, &arg->attr, version); + arg->ino = node->ino; + arg->generation = node->generation; + if(f->flags & FUSE_DEBUG) { + printf(" INO: %li\n", arg->ino); + fflush(stdout); + } + } + return res; +} + 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; res = -ENOENT; @@ -423,19 +442,9 @@ static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name) } res = -ENOSYS; if(f->op.getattr) - res = f->op.getattr(path, &buf); + res = lookup_path(f, in->ino, in->unique, name, path, &arg); free(path); } - - if(res == 0) { - memset(&arg, 0, sizeof(struct fuse_lookup_out)); - convert_stat(&buf, &arg.attr); - arg.ino = find_node(f, in->ino, name, &arg.attr, in->unique); - if(f->flags & FUSE_DEBUG) { - printf(" LOOKUP: %li\n", arg.ino); - fflush(stdout); - } - } send_reply(f, in, res, &arg, sizeof(arg)); } @@ -609,27 +618,24 @@ static void do_mknod(struct fuse *f, struct fuse_in_header *in, { int res; char *path; - struct fuse_mknod_out outarg; - struct stat buf; + char *name = PARAM(inarg); + struct fuse_lookup_out outarg; res = -ENOENT; - path = get_path_name(f, in->ino, PARAM(inarg)); + path = get_path_name(f, in->ino, name); if(path != NULL) { + if(f->flags & FUSE_DEBUG) { + printf("MKNOD %s\n", path); + fflush(stdout); + } 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); + res = lookup_path(f, in->ino, in->unique, name, path, &outarg); } free(path); } - if(res == 0) { - memset(&outarg, 0, sizeof(struct fuse_mknod_out)); - convert_stat(&buf, &outarg.attr); - outarg.ino = find_node(f, in->ino, PARAM(inarg), &outarg.attr, - in->unique); - } - send_reply(f, in, res, &outarg, sizeof(outarg)); } @@ -638,16 +644,25 @@ static void do_mkdir(struct fuse *f, struct fuse_in_header *in, { int res; char *path; + char *name = PARAM(inarg); + struct fuse_lookup_out outarg; res = -ENOENT; - path = get_path_name(f, in->ino, PARAM(inarg)); + path = get_path_name(f, in->ino, name); if(path != NULL) { + if(f->flags & FUSE_DEBUG) { + printf("MKDIR %s\n", path); + fflush(stdout); + } res = -ENOSYS; - if(f->op.mkdir) + if(f->op.mkdir && f->op.getattr) { res = f->op.mkdir(path, inarg->mode); + if(res == 0) + res = lookup_path(f, in->ino, in->unique, name, path, &outarg); + } free(path); } - send_reply(f, in, res, NULL, 0); + send_reply(f, in, res, &outarg, sizeof(outarg)); } static void do_remove(struct fuse *f, struct fuse_in_header *in, char *name) @@ -679,16 +694,24 @@ static void do_symlink(struct fuse *f, struct fuse_in_header *in, char *name, { int res; char *path; + struct fuse_lookup_out outarg; res = -ENOENT; path = get_path_name(f, in->ino, name); if(path != NULL) { + if(f->flags & FUSE_DEBUG) { + printf("SYMLINK %s\n", path); + fflush(stdout); + } res = -ENOSYS; - if(f->op.symlink) + if(f->op.symlink && f->op.getattr) { res = f->op.symlink(link, path); + if(res == 0) + res = lookup_path(f, in->ino, in->unique, name, path, &outarg); + } free(path); } - send_reply(f, in, res, NULL, 0); + send_reply(f, in, res, &outarg, sizeof(outarg)); } static void do_rename(struct fuse *f, struct fuse_in_header *in, @@ -725,20 +748,30 @@ static void do_link(struct fuse *f, struct fuse_in_header *in, int res; char *oldpath; char *newpath; + char *name = PARAM(arg); + struct fuse_lookup_out outarg; res = -ENOENT; oldpath = get_path(f, in->ino); if(oldpath != NULL) { - newpath = get_path_name(f, arg->newdir, PARAM(arg)); + newpath = get_path_name(f, arg->newdir, name); if(newpath != NULL) { + if(f->flags & FUSE_DEBUG) { + printf("LINK %s\n", newpath); + fflush(stdout); + } res = -ENOSYS; - if(f->op.link) + if(f->op.link && f->op.getattr) { res = f->op.link(oldpath, newpath); + if(res == 0) + res = lookup_path(f, arg->newdir, in->unique, name, + newpath, &outarg); + } free(newpath); } free(oldpath); } - send_reply(f, in, res, NULL, 0); + send_reply(f, in, res, &outarg, sizeof(outarg)); } static void do_open(struct fuse *f, struct fuse_in_header *in, @@ -1090,6 +1123,7 @@ struct fuse *fuse_new(int fd, int flags, const struct fuse_operations *op) f->flags = flags; f->fd = fd; f->ctr = 0; + f->generation = 0; /* FIXME: Dynamic hash table */ f->name_table_size = 14057; f->name_table = (struct node **) @@ -1111,7 +1145,9 @@ struct fuse *fuse_new(int fd, int flags, const struct fuse_operations *op) root->rdev = 0; root->name = strdup("/"); root->parent = 0; - hash_ino(f, root, FUSE_ROOT_INO); + root->ino = FUSE_ROOT_INO; + root->generation = 0; + hash_ino(f, root); return f; } diff --git a/lib/fuse_i.h b/lib/fuse_i.h index 52f1fdc..4eaa5ba 100644 --- a/lib/fuse_i.h +++ b/lib/fuse_i.h @@ -16,6 +16,7 @@ struct node { struct node *name_next; struct node *ino_next; fino_t ino; + unsigned int generation; fino_t parent; char *name; int mode; @@ -32,6 +33,7 @@ struct fuse { struct node **ino_table; size_t ino_table_size; fino_t ctr; + unsigned int generation; pthread_mutex_t lock; int numworker; int numavail;