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 <mszeredi@inf.bme.hu>
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 @@
struct fuse_lookup_out {
unsigned long ino;
+ unsigned long generation;
struct fuse_attr attr;
};
@@ -133,19 +134,13 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
/**
* 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 @@
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 @@
{
__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 @@
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 @@
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 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 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 @@
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 @@
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 @@
}
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 @@
{
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 @@
{
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 @@
{
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 @@
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 @@
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 @@
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 *name_next;
struct node *ino_next;
fino_t ino;
+ unsigned int generation;
fino_t parent;
char *name;
int mode;
@@ -32,6 +33,7 @@
struct node **ino_table;
size_t ino_table_size;
fino_t ctr;
+ unsigned int generation;
pthread_mutex_t lock;
int numworker;
int numavail;