fixes
authorMiklos Szeredi <miklos@szeredi.hu>
Mon, 21 Jun 2004 09:45:30 +0000 (09:45 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Mon, 21 Jun 2004 09:45:30 +0000 (09:45 +0000)
ChangeLog
kernel/dir.c
kernel/fuse_i.h
kernel/inode.c
lib/fuse.c

index 6a595fb3bfe1d37d33d74e18c6970a3a6606bdca..b6bb446e17f8e265a506a41bfe6ad22835224c5e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2004-06-21  Miklos Szeredi <mszeredi@inf.bme.hu>
+
+       * Fix possible inode leak in userspace in case of unfinished
+       lookup/mknod/mkdir/symlink/link operation.
+       
 2004-06-20  Miklos Szeredi <mszeredi@inf.bme.hu>
 
        * Fix some races and cleanups in fuse_read_super()
index 2cf0676286bbfcccb577cf1f4ca0b7bc9e28c290..59cab49a9e34956e65b0231a08242da868b67b11 100644 (file)
@@ -102,6 +102,23 @@ struct inode *fuse_iget(struct super_block *sb, ino_t ino, int generation,
        return inode;
 }
 
+static int fuse_send_lookup(struct fuse_conn *fc, struct fuse_req *req,
+                           struct inode *dir, struct dentry *entry, 
+                           struct fuse_entry_out *outarg, int *version)
+{
+       req->in.h.opcode = FUSE_LOOKUP;
+       req->in.h.ino = dir->i_ino;
+       req->in.numargs = 1;
+       req->in.args[0].size = entry->d_name.len + 1;
+       req->in.args[0].value = entry->d_name.name;
+       req->out.numargs = 1;
+       req->out.args[0].size = sizeof(struct fuse_entry_out);
+       req->out.args[0].value = outarg;
+       request_send(fc, req);
+       *version = req->out.h.unique;
+       return req->out.h.error;
+}
+
 static int fuse_do_lookup(struct inode *dir, struct dentry *entry,
                          struct fuse_entry_out *outarg, int *version)
 {
@@ -114,17 +131,8 @@ static int fuse_do_lookup(struct inode *dir, struct dentry *entry,
        req = fuse_get_request(fc);
        if (!req)
                return -ERESTARTSYS;
-       req->in.h.opcode = FUSE_LOOKUP;
-       req->in.h.ino = dir->i_ino;
-       req->in.numargs = 1;
-       req->in.args[0].size = entry->d_name.len + 1;
-       req->in.args[0].value = entry->d_name.name;
-       req->out.numargs = 1;
-       req->out.args[0].size = sizeof(struct fuse_entry_out);
-       req->out.args[0].value = outarg;
-       request_send(fc, req);
-       *version = req->out.h.unique;
-       err = req->out.h.error;
+       
+       err = fuse_send_lookup(fc, req, dir, entry, outarg, version);
        fuse_put_request(fc, req);
        return err;
 }
@@ -142,18 +150,30 @@ static inline unsigned long time_to_jiffies(unsigned long sec,
 static int fuse_lookup_iget(struct inode *dir, struct dentry *entry,
                            struct inode **inodep)
 {
+       struct fuse_conn *fc = INO_FC(dir);
        int err;
        struct fuse_entry_out outarg;
        int version;
        struct inode *inode = NULL;
+       struct fuse_req *req;
+
+       if (entry->d_name.len > FUSE_NAME_MAX)
+               return -ENAMETOOLONG;
+       req = fuse_get_request(fc);
+       if (!req)
+               return -ERESTARTSYS;
 
-       err = fuse_do_lookup(dir, entry, &outarg, &version);
+       err = fuse_send_lookup(fc, req, dir, entry, &outarg, &version);
        if (!err) {
                inode = fuse_iget(dir->i_sb, outarg.ino, outarg.generation,
                                  &outarg.attr, version);
-               if (!inode)
+               if (!inode) {
+                       fuse_send_forget(fc, req, outarg.ino, version);
                        return -ENOMEM;
-       } else if (err != -ENOENT)
+               }
+       } 
+       fuse_put_request(fc, req);
+       if (err && err != -ENOENT)
                return err;
 
        entry->d_time = time_to_jiffies(outarg.entry_valid,
@@ -163,15 +183,19 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry,
        return 0;
 }
 
-static int lookup_new_entry(struct inode *dir, struct dentry *entry,
-                                  struct fuse_entry_out *outarg, int version,
-                                  int mode)
+static int lookup_new_entry(struct fuse_conn *fc, struct fuse_req *req,
+                           struct inode *dir, struct dentry *entry,
+                           struct fuse_entry_out *outarg, int version,
+                           int mode)
 {
        struct inode *inode;
        inode = fuse_iget(dir->i_sb, outarg->ino, outarg->generation, 
                          &outarg->attr, version);
-       if (!inode) 
+       if (!inode) {
+               fuse_send_forget(fc, req, outarg->ino, version);
                return -ENOMEM;
+       }
+       fuse_put_request(fc, req);
 
        /* Don't allow userspace to do really stupid things... */
        if ((inode->i_mode ^ mode) & S_IFMT) {
@@ -213,9 +237,10 @@ static int _fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
        request_send(fc, req);
        err = req->out.h.error;
        if (!err)
-               err = lookup_new_entry(dir, entry, &outarg, req->out.h.unique,
-                                      mode);
-       fuse_put_request(fc, req);
+               err = lookup_new_entry(fc, req, dir, entry, &outarg,
+                                      req->out.h.unique, mode);
+       else
+               fuse_put_request(fc, req);
        return err;
 }
 
@@ -251,9 +276,10 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode)
        request_send(fc, req);
        err = req->out.h.error;
        if (!err)
-               err = lookup_new_entry(dir, entry, &outarg, req->out.h.unique,
-                                      S_IFDIR);
-       fuse_put_request(fc, req);
+               err = lookup_new_entry(fc, req, dir, entry, &outarg,
+                                      req->out.h.unique, S_IFDIR);
+       else
+               fuse_put_request(fc, req);
        return err;
 }
 
@@ -286,9 +312,10 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry,
        request_send(fc, req);
        err = req->out.h.error;
        if (!err)
-               err = lookup_new_entry(dir, entry, &outarg, req->out.h.unique,
-                                      S_IFLNK);
-       fuse_put_request(fc, req);
+               err = lookup_new_entry(fc, req, dir, entry, &outarg,
+                                      req->out.h.unique, S_IFLNK);
+       else
+               fuse_put_request(fc, req);
        return err;
 }
 
@@ -399,10 +426,10 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
        if (!err) {
                /* Invalidate old entry, so attributes are refreshed */
                d_invalidate(entry);
-               err = lookup_new_entry(newdir, newent, &outarg,
+               err = lookup_new_entry(fc, req, newdir, newent, &outarg,
                                       req->out.h.unique, inode->i_mode);
-       }
-       fuse_put_request(fc, req);
+       } else
+               fuse_put_request(fc, req);
        return err;
 }
 
index f9daa7e9d6fa3302721379fcddaa9f4a8d2c3ff2..7055d1783a1e7edfdd881768db56b23c9054b5f5 100644 (file)
@@ -228,6 +228,12 @@ struct inode *fuse_iget(struct super_block *sb, ino_t ino, int generation,
                        struct fuse_attr *attr, int version);
 
 
+/**
+ * Send FORGET command
+ */
+void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, ino_t ino,
+                     int version);
+
 /**
  * Initialise operations on regular file
  */
index d04d63603e2cab142e110bdd965e2c1c7b2d2048..83ac407340c2b5e6e7bcd009ee9ec1570d4c2738 100644 (file)
@@ -44,24 +44,29 @@ static void fuse_read_inode(struct inode *inode)
        /* No op */
 }
 
+void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, ino_t ino,
+                     int version)
+{
+       struct fuse_forget_in *inarg = &req->misc.forget_in;
+       inarg->version = version;
+       req->in.h.opcode = FUSE_FORGET;
+       req->in.h.ino = ino;
+       req->in.numargs = 1;
+       req->in.args[0].size = sizeof(struct fuse_forget_in);
+       req->in.args[0].value = inarg;
+       request_send_noreply(fc, req);
+}
+
 static void fuse_clear_inode(struct inode *inode)
 {
        struct fuse_conn *fc = INO_FC(inode);
        struct fuse_req *req;
-       struct fuse_forget_in *inarg = NULL;
        
        if (fc == NULL)
                return;
 
        req = fuse_get_request_nonint(fc);
-       inarg = &req->misc.forget_in;
-       inarg->version = inode->i_version;
-       req->in.h.opcode = FUSE_FORGET;
-       req->in.h.ino = inode->i_ino;
-       req->in.numargs = 1;
-       req->in.args[0].size = sizeof(struct fuse_forget_in);
-       req->in.args[0].value = inarg;
-       request_send_noreply(fc, req);
+       fuse_send_forget(fc, req, inode->i_ino, inode->i_version);
 }
 
 static void fuse_put_super(struct super_block *sb)
index 4015d827376ea441f68fe05b3c45b7a2e1aefe0f..eeae295a9d6fe689a12767226cca8afe4d5f93e6 100644 (file)
@@ -444,6 +444,7 @@ static int lookup_path(struct fuse *f, fino_t ino, int version,  char *name,
 static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name)
 {
     int res;
+    int res2;
     char *path;
     struct fuse_entry_out arg;
 
@@ -459,7 +460,9 @@ static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name)
             res = lookup_path(f, in->ino, in->unique, name, path, &arg);
         free(path);
     }
-    send_reply(f, in, res, &arg, sizeof(arg));
+    res2 = send_reply(f, in, res, &arg, sizeof(arg));
+    if (res == 0 && res2 == -ENOENT)
+        destroy_node(f, arg.ino, in->unique);
 }
 
 static void do_forget(struct fuse *f, struct fuse_in_header *in,
@@ -636,6 +639,7 @@ static void do_mknod(struct fuse *f, struct fuse_in_header *in,
                      struct fuse_mknod_in *inarg)
 {
     int res;
+    int res2;
     char *path;
     char *name = PARAM(inarg);
     struct fuse_entry_out outarg;
@@ -655,13 +659,16 @@ static void do_mknod(struct fuse *f, struct fuse_in_header *in,
         }
         free(path);
     }
-    send_reply(f, in, res, &outarg, sizeof(outarg));
+    res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
+    if (res == 0 && res2 == -ENOENT)
+        destroy_node(f, outarg.ino, in->unique);
 }
 
 static void do_mkdir(struct fuse *f, struct fuse_in_header *in,
                      struct fuse_mkdir_in *inarg)
 {
     int res;
+    int res2;
     char *path;
     char *name = PARAM(inarg);
     struct fuse_entry_out outarg;
@@ -681,7 +688,9 @@ static void do_mkdir(struct fuse *f, struct fuse_in_header *in,
         }
         free(path);
     }
-    send_reply(f, in, res, &outarg, sizeof(outarg));
+    res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
+    if (res == 0 && res2 == -ENOENT)
+        destroy_node(f, outarg.ino, in->unique);
 }
 
 static void do_unlink(struct fuse *f, struct fuse_in_header *in, char *name)
@@ -726,6 +735,7 @@ static void do_symlink(struct fuse *f, struct fuse_in_header *in, char *name,
                        char *link)
 {
     int res;
+    int res2;
     char *path;
     struct fuse_entry_out outarg;
 
@@ -744,7 +754,10 @@ static void do_symlink(struct fuse *f, struct fuse_in_header *in, char *name,
         }
         free(path);
     }
-    send_reply(f, in, res, &outarg, sizeof(outarg));
+    res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
+    if (res == 0 && res2 == -ENOENT)
+        destroy_node(f, outarg.ino, in->unique);
+
 }
 
 static void do_rename(struct fuse *f, struct fuse_in_header *in,
@@ -779,6 +792,7 @@ static void do_link(struct fuse *f, struct fuse_in_header *in,
                     struct fuse_link_in *arg)
 {
     int res;
+    int res2;
     char *oldpath;
     char *newpath;
     char *name = PARAM(arg);
@@ -804,7 +818,9 @@ static void do_link(struct fuse *f, struct fuse_in_header *in,
         }
         free(oldpath);
     }
-    send_reply(f, in, res, &outarg, sizeof(outarg));   
+    res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
+    if (res == 0 && res2 == -ENOENT)
+        destroy_node(f, outarg.ino, in->unique);
 }
 
 static void do_open(struct fuse *f, struct fuse_in_header *in,