improvements to the kernel interface
authorMiklos Szeredi <miklos@szeredi.hu>
Thu, 19 Feb 2004 16:55:40 +0000 (16:55 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Thu, 19 Feb 2004 16:55:40 +0000 (16:55 +0000)
ChangeLog
include/linux/fuse.h
kernel/dir.c
kernel/fuse_i.h
kernel/inode.c
lib/fuse.c
lib/fuse_i.h

index 54cfd16a54e527fe76e71b28c922fda085bd79ca..cdc25791795eaeb7399cb6e7adf63d4c374dbf65 100644 (file)
--- 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>
 
index 2b516a5782683079222e51edbdc73edd81085025..9bca31c7a5ba717b1b05d75e2f522c84943a2d4a 100644 (file)
@@ -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 {
index 5fec669bb1f86c2abe367a41acfce8e14fa055b7..b9c26e60ebb84c38d597789ead3e8013557244b9 100644 (file)
@@ -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)
index d4a677ed494e28d0b82122e16bb6cb4dc7c57e78..48e52c927c5138b98ad2110059f896946ed7acbf 100644 (file)
@@ -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);
 
 
index 9ab0dc3df3a7305e380de2e40d26abc4eb9f4bfc..08bf3c982c3b8365730e78ce5ce03e82f506dcfb 100644 (file)
@@ -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);
index ffb348934ac5edda819c1e8047c483d66beabfbc..079220a0eafa2837ffd2b623c7eb04d3ad488c8d 100644 (file)
@@ -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;
 }
index 52f1fdc6996918391bcdc37888a0d7839dd9b07b..4eaa5bad5208916054b82e62d4c12ff2066485f3 100644 (file)
@@ -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;