x
authorMiklos Szeredi <miklos@szeredi.hu>
Tue, 30 Oct 2001 15:06:52 +0000 (15:06 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Tue, 30 Oct 2001 15:06:52 +0000 (15:06 +0000)
fusepro.c
include/fuse.h
include/linux/fuse.h
kernel/dev.c
kernel/dir.c
kernel/inode.c
lib/fuse.c

index cbe80aabf0d180f280d73a0d89971eb6c447f248..4f593e7a0ca8f1d8d403f5f98b0d046d2ad71fa1 100644 (file)
--- a/fusepro.c
+++ b/fusepro.c
@@ -108,6 +108,28 @@ static int pro_rmdir(const char *path)
     return 0;
 }
 
+static int pro_rename(const char *from, const char *to)
+{
+    int res;
+
+    res = rename(from, to);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int pro_link(const char *from, const char *to)
+{
+    int res;
+
+    res = link(from, to);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
 static void exit_handler()
 {
     exit(0);
@@ -152,6 +174,8 @@ static struct fuse_operations pro_oper = {
     symlink:   pro_symlink,
     unlink:    pro_unlink,
     rmdir:     pro_rmdir,
+    rename:     pro_rename,
+    link:      pro_link,
 };
 
 int main(int argc, char *argv[])
index 6837b9ef40d83163d953f7fddf5b2a46ff9362ee..e18f606164d067eb9e42aea60b1deba6f8874a5a 100644 (file)
@@ -23,9 +23,11 @@ struct fuse_operations {
     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 (*symlink) (const char *from, const char *to);
     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);
 };
 
 struct fuse *fuse_new();
index b519a8d47d13d2ef6dd03d820f26260c929505b0..64a4af371f42bb792ea748005c78438d9d2e3f4a 100644 (file)
@@ -44,6 +44,7 @@ enum fuse_opcode {
        FUSE_UNLINK,
        FUSE_RMDIR,
        FUSE_RENAME,
+       FUSE_LINK,
 };
 
 /* Conservative buffer size for the client */
@@ -79,10 +80,14 @@ struct fuse_mkdir_in {
        char name[1];
 };
 
-
 struct fuse_rename_in {
        unsigned long newdir;
-       char names[2];
+       char names[1];
+};
+
+struct fuse_link_in {
+       unsigned long newdir;
+       char name[1];
 };
 
 struct fuse_in_header {
@@ -93,7 +98,7 @@ struct fuse_in_header {
 
 struct fuse_out_header {
        int unique;
-       int result;
+       int error;
 };
 
 struct fuse_dirent {
index a91a1762e30b5d28987da93758bdc8fdaf31d215..d32589d2d21e5faa06e584d8fd554cc944136d19 100644 (file)
@@ -53,14 +53,14 @@ static int request_check(struct fuse_req *req, struct fuse_out *outp)
        oh = (struct fuse_out_header *) req->out;
        size = req->outsize - OHSIZE;
        
-       if (oh->result <= -512 || oh->result > 0) {
-               printk("fuse: bad result\n");
+       if (oh->error <= -512 || oh->error > 0) {
+               printk("fuse: bad error value: %i\n", oh->error);
                return -EPROTO;
        }
 
        if(size > outp->argsize || 
-          (oh->result == 0 && !outp->argvar && size != outp->argsize) ||
-          (oh->result != 0 && size != 0)) {
+          (oh->error == 0 && !outp->argvar && size != outp->argsize) ||
+          (oh->error != 0 && size != 0)) {
                printk("fuse: invalid argument length: %i (%i)\n", size,
                       req->opcode);
                return -EPROTO;
@@ -71,7 +71,7 @@ static int request_check(struct fuse_req *req, struct fuse_out *outp)
        if(size)
                memcpy(outp->arg, req->out + OHSIZE, size);
        
-       return oh->result;
+       return oh->error;
 }
 
 static void request_free(struct fuse_req *req)
@@ -158,7 +158,7 @@ void request_send(struct fuse_conn *fc, struct fuse_in *inp,
        spin_unlock(&fuse_lock);
   out:
        if(outp)
-               outp->h.result = ret;
+               outp->h.error = ret;
 }
 
 static int request_wait(struct fuse_conn *fc)
index 065ac0187e242cad9cc77a0f4d0b8730ec9434b6..b23def74f8ec46fed7b2f0bed23f727578dff343 100644 (file)
@@ -22,6 +22,7 @@ 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)
 {
@@ -57,7 +58,6 @@ void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
        }
 }
 
-
 static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
 {
        struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
@@ -74,19 +74,18 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
        out.arg = &arg;
        request_send(fc, &in, &out);
        
-       if(out.h.result) {
-               /* Negative dentries are not hashed */
-               if(out.h.result == -ENOENT)
-                       return NULL;
-               else
-                       return ERR_PTR(out.h.result);
+       inode = NULL;
+       if(!out.h.error) {
+               inode = iget(dir->i_sb, arg.ino);
+               if(!inode) 
+                       return ERR_PTR(-ENOMEM);
+               
+               fuse_init_inode(inode, &arg.attr);
        }
+       else if(out.h.error != -ENOENT)
+               return ERR_PTR(out.h.error);
 
-       inode = iget(dir->i_sb, arg.ino);
-       if(!inode) 
-               return ERR_PTR(-ENOMEM);
-
-       fuse_init_inode(inode, &arg.attr);
+       entry->d_op = &fuse_dentry_opertations;
        d_add(entry, inode);
        return NULL;
 }
@@ -121,15 +120,15 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
        request_send(fc, &in, &out);
        kfree(inarg);
 
-       if(out.h.result
-               return out.h.result;
+       if(out.h.error
+               return out.h.error;
 
        inode = iget(dir->i_sb, outarg.ino);
        if(!inode) 
                return -ENOMEM;
        
        fuse_init_inode(inode, &outarg.attr);
-       d_add(entry, inode);
+       d_instantiate(entry, inode);
 
        return 0;
 }
@@ -163,7 +162,7 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode)
        request_send(fc, &in, &out);
        kfree(inarg);
 
-       return out.h.result;
+       return out.h.error;
 }
 
 static int fuse_symlink(struct inode *dir, struct dentry *entry,
@@ -190,7 +189,7 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry,
        request_send(fc, &in, &out);
        kfree(inarg);
        
-       return out.h.result;
+       return out.h.error;
 }
 
 static int fuse_remove(struct inode *dir, struct dentry *entry, 
@@ -205,12 +204,7 @@ static int fuse_remove(struct inode *dir, struct dentry *entry,
        in.argsize = entry->d_name.len + 1;
        in.arg = entry->d_name.name;
        request_send(fc, &in, &out);
-       if(!out.h.result) {
-               d_drop(entry);
-               entry->d_inode->i_nlink = 0;
-       }
-
-       return out.h.result;
+       return out.h.error;
 }
 
 static int fuse_unlink(struct inode *dir, struct dentry *entry)
@@ -251,7 +245,35 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
        request_send(fc, &in, &out);
        kfree(inarg);
 
-       return out.h.result;
+       return out.h.error;
+}
+
+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_in in = FUSE_IN_INIT;
+       struct fuse_out out = FUSE_OUT_INIT;
+       struct fuse_link_in *inarg;
+       unsigned int insize;
+       
+       insize = offsetof(struct fuse_link_in, name) + newent->d_name.len + 1;
+       inarg = kmalloc(insize, GFP_KERNEL);
+       if(!inarg)
+               return -ENOMEM;
+       
+       inarg->newdir = newdir->i_ino;
+       strcpy(inarg->name, newent->d_name.name);
+
+       in.h.opcode = FUSE_LINK;
+       in.h.ino = inode->i_ino;
+       in.argsize = insize;
+       in.arg = inarg;
+       request_send(fc, &in, &out);
+       kfree(inarg);
+
+       return out.h.error;
 }
 
 static int fuse_permission(struct inode *inode, int mask)
@@ -259,6 +281,8 @@ 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)
 {
        struct inode *inode = dentry->d_inode;
@@ -267,18 +291,19 @@ static int fuse_revalidate(struct dentry *dentry)
        struct fuse_out out = FUSE_OUT_INIT;
        struct fuse_getattr_out arg;
        
+       if(inode->i_ino != FUSE_ROOT_INO)
+               return 0;
+
        in.h.opcode = FUSE_GETATTR;
        in.h.ino = inode->i_ino;
        out.argsize = sizeof(arg);
        out.arg = &arg;
        request_send(fc, &in, &out);
        
-       if(out.h.result == 0)
+       if(!out.h.error)
                change_attributes(inode, &arg.attr);
-       else
-               d_drop(dentry);
-
-       return out.h.result;
+       
+       return out.h.error;
 }
 
 static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
@@ -347,9 +372,9 @@ static int read_link(struct dentry *dentry, char **bufp)
        out.argsize = PAGE_SIZE - 1;
        out.argvar = 1;
        request_send(fc, &in, &out);
-       if(out.h.result) {
+       if(out.h.error) {
                free_page(page);
-               return out.h.result;
+               return out.h.error;
        }
 
        *bufp = (char *) page;
@@ -405,7 +430,7 @@ static int fuse_dir_open(struct inode *inode, struct file *file)
        out.argsize = sizeof(outarg);
        out.arg = &outarg;
        request_send(fc, &in, &out);
-       if(out.h.result == 0) {
+       if(!out.h.error) {
                struct file *cfile = outarg.file;
                struct inode *inode;
                if(!cfile) {
@@ -422,7 +447,7 @@ static int fuse_dir_open(struct inode *inode, struct file *file)
                file->private_data = cfile;
        }
 
-       return out.h.result;
+       return out.h.error;
 }
 
 static int fuse_dir_release(struct inode *inode, struct file *file)
@@ -437,6 +462,14 @@ static int fuse_dir_release(struct inode *inode, struct file *file)
        return 0;
 }
 
+static int fuse_dentry_revalidate(struct dentry *entry, int flags)
+{
+       if(!entry->d_inode || !(flags & LOOKUP_CONTINUE))
+               return 0;
+       else
+               return 1;
+}
+
 static struct inode_operations fuse_dir_inode_operations =
 {
        lookup:         fuse_lookup,
@@ -447,12 +480,12 @@ static struct inode_operations fuse_dir_inode_operations =
        unlink:         fuse_unlink,
        rmdir:          fuse_rmdir,
        rename:         fuse_rename,
-#if 0
        link:           fuse_link,
+#if 0
        setattr:        fuse_setattr,
 #endif
        permission:     fuse_permission,
-        revalidate:    fuse_revalidate,
+       revalidate:     fuse_revalidate,
 };
 
 static struct file_operations fuse_dir_operations = {
@@ -464,12 +497,12 @@ static struct file_operations fuse_dir_operations = {
 
 static struct inode_operations fuse_file_inode_operations = {
        permission:     fuse_permission,
-        revalidate:    fuse_revalidate,
+       revalidate:     fuse_revalidate,
 };
 
 static struct inode_operations fuse_special_inode_operations = {
        permission:     fuse_permission,
-        revalidate:    fuse_revalidate,
+       revalidate:     fuse_revalidate,
 };
 
 static struct file_operations fuse_file_operations = {
@@ -479,7 +512,11 @@ static struct inode_operations fuse_symlink_inode_operations =
 {
        readlink:       fuse_readlink,
        follow_link:    fuse_follow_link,
-        revalidate:    fuse_revalidate,
+       revalidate:     fuse_revalidate,
+};
+
+static struct dentry_operations fuse_dentry_opertations = {
+       d_revalidate:   fuse_dentry_revalidate,
 };
 
 /* 
index b59a1468e4a680bef1127e7418cb362449bd621b..702ef5792e5ae01a3a3cbbbe0693f7c21fcb82aa 100644 (file)
@@ -182,6 +182,9 @@ static struct super_block *fuse_read_super(struct super_block *sb,
 
         sb->u.generic_sbp = fc;
        sb->s_root = d_alloc_root(root);
+       if(!sb->s_root)
+               goto err;
+
        fc->sb = sb;
        spin_unlock(&fuse_lock);
        
index 62695f0923a1ce5e2248e9441b73b5cf6d27d79c..f033e0406b5b33b31804a369d5a1123d46f3be97 100644 (file)
@@ -12,6 +12,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
+#include <assert.h>
 
 
 static guint name_hash(const struct node *node)
@@ -29,7 +30,7 @@ static gint name_compare(const struct node *node1, const struct node *node2)
 static struct node *new_node(fino_t parent, const char *name)
 {
     struct node *node = g_new0(struct node, 1);
-    node->name = strdup(name);
+    node->name = g_strdup(name);
     node->parent = parent;
     return node;
 }
@@ -51,15 +52,22 @@ static inline fino_t get_ino(struct node *node)
     return (((fino_t) node) - 0x8000000) >> 3;
 }
 
-static fino_t find_node(struct fuse *f, fino_t parent, char *name, int create)
+static struct node *lookup_node(struct fuse *f, fino_t parent,
+                                const char *name)
 {
-    struct node *node;
     struct node tmp;
-    
-    tmp.name = name;
+
+    tmp.name = (char *) name;
     tmp.parent = parent;
 
-    node = g_hash_table_lookup(f->nametab, &tmp);
+    return g_hash_table_lookup(f->nametab, &tmp);
+}
+
+static fino_t find_node(struct fuse *f, fino_t parent, char *name, int create)
+{
+    struct node *node;
+
+    node = lookup_node(f, parent, name);
     if(node != NULL)
         return get_ino(node);
 
@@ -109,6 +117,27 @@ static void remove_node(struct fuse *f, fino_t ino)
     free_node(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);
+        
+    /* 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);
+    node->parent = newdir;
+    g_hash_table_insert(f->nametab, node, node);
+}
+
+
 static void convert_stat(struct stat *stbuf, struct fuse_attr *attr)
 {
     attr->mode    = stbuf->st_mode;
@@ -124,23 +153,6 @@ static void convert_stat(struct stat *stbuf, struct fuse_attr *attr)
     attr->ctime   = stbuf->st_ctime;
 }
 
-static int get_attributes(struct fuse *f, fino_t ino, struct fuse_attr *attr)
-{
-    char *path;
-    struct stat buf;
-    int res;
-
-    path = get_path(ino);
-    res = -ENOSYS;
-    if(f->op.getattr)
-        res = f->op.getattr(path, &buf);
-    g_free(path);
-    if(res == 0) 
-        convert_stat(&buf, attr);
-    
-    return res;
-}
-
 static int fill_dir(struct fuse_dh *dh, char *name, int type)
 {
     struct fuse_dirent dirent;
@@ -160,7 +172,7 @@ static int fill_dir(struct fuse_dh *dh, char *name, int type)
     return 0;
 }
 
-static void send_reply(struct fuse *f, struct fuse_in_header *in, int result,
+static void send_reply(struct fuse *f, struct fuse_in_header *in, int error,
                        void *arg, size_t argsize)
 {
     int res;
@@ -168,25 +180,24 @@ static void send_reply(struct fuse *f, struct fuse_in_header *in, int result,
     size_t outsize;
     struct fuse_out_header *out;
 
-    if(result > 0) {
-        fprintf(stderr, "positive result to operation %i : %i\n", in->opcode,
-                result);
-        result = -ERANGE;
+    if(error > 0) {
+        fprintf(stderr, "positive error code: %i\n",  error);
+        error = -ERANGE;
     }
 
-    if(result != 0)
+    if(error)
         argsize = 0;
 
     outsize = sizeof(struct fuse_out_header) + argsize;
     outbuf = (char *) g_malloc(outsize);
     out = (struct fuse_out_header *) outbuf;
     out->unique = in->unique;
-    out->result = result;
+    out->error = error;
     if(argsize != 0)
         memcpy(outbuf + sizeof(struct fuse_out_header), arg, argsize);
 
-    printf("   unique: %i, result: %i (%s), outsize: %i\n", out->unique,
-           out->result, strerror(-out->result), outsize);
+    printf("   unique: %i, error: %i (%s), outsize: %i\n", out->unique,
+           out->error, strerror(-out->error), outsize);
                 
     res = write(f->fd, outbuf, outsize);
     if(res == -1)
@@ -198,11 +209,19 @@ static void send_reply(struct fuse *f, struct fuse_in_header *in, int result,
 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;
 
-    arg.ino = find_node(f, in->ino, name, 1);
-    res = get_attributes(f, arg.ino, &arg.attr);
-
+    path = get_path_name(in->ino, name);
+    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);
+    }
     send_reply(f, in, res, &arg, sizeof(arg));
 }
 
@@ -217,9 +236,18 @@ static void do_forget(struct fuse *f, unsigned long *inos, size_t num)
 static void do_getattr(struct fuse *f, struct fuse_in_header *in)
 {
     int res;
+    char *path;
+    struct stat buf;
     struct fuse_getattr_out arg;
 
-    res = get_attributes(f, in->ino, &arg.attr);
+    path = get_path(in->ino);
+    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));
 }
 
@@ -269,8 +297,7 @@ static void do_mknod(struct fuse *f, struct fuse_in_header *in,
     struct fuse_mknod_out outarg;
     struct stat buf;
 
-    outarg.ino = find_node(f, in->ino, inarg->name, 1);
-    path = get_path(outarg.ino);
+    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);
@@ -278,9 +305,10 @@ static void do_mknod(struct fuse *f, struct fuse_in_header *in,
             res = f->op.getattr(path, &buf);
     }
     g_free(path);
-
-    if(res == 0)
+    if(res == 0) {
         convert_stat(&buf, &outarg.attr);
+        outarg.ino = find_node(f, in->ino, inarg->name, 1);
+    }
 
     send_reply(f, in, res, &outarg, sizeof(outarg));
 }
@@ -332,6 +360,39 @@ static void do_symlink(struct fuse *f, struct fuse_in_header *in, char *name,
     send_reply(f, in, res, NULL, 0);
 }
 
+static void do_rename(struct fuse *f, struct fuse_in_header *in,
+                      struct fuse_rename_in *inarg)
+{
+    int res;
+    fino_t olddir = in->ino;
+    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);
+    send_reply(f, in, res, NULL, 0);   
+}
+
+static void do_link(struct fuse *f, struct fuse_in_header *in,
+                    struct fuse_link_in *inarg)
+{
+    int res;
+    char *oldpath = get_path(in->ino);
+    char *newpath = get_path_name(inarg->newdir, inarg->name);
+
+    res = -ENOSYS;
+    if(f->op.link)
+        res = f->op.link(oldpath, newpath);
+
+    send_reply(f, in, res, NULL, 0);   
+}
+
 
 void fuse_loop(struct fuse *f)
 {
@@ -400,6 +461,14 @@ void fuse_loop(struct fuse *f)
                        ((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;
+
         default:
             fprintf(stderr, "Operation %i not implemented\n", in->opcode);
             /* No need to send reply to async requests */