fix release/forget race
authorMiklos Szeredi <miklos@szeredi.hu>
Tue, 6 Jul 2004 13:16:30 +0000 (13:16 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Tue, 6 Jul 2004 13:16:30 +0000 (13:16 +0000)
ChangeLog
include/linux/fuse.h
kernel/dev.c
kernel/file.c
kernel/fuse_i.h
lib/fuse.c

index f38ce27c909e8f0506c9e9d722c2fb655e43118a..b3676b8a7eaf6317aea99ff7ebb222146fa9c678 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2004-07-06  Miklos Szeredi <mszeredi@inf.bme.hu>
+
+       * Make RELEASE method synchronous.  This fixes an abort in libfuse
+       if FORGET was received earlier than the last RELEASE (bug found
+       with the LTP test-suite)
+
 2004-07-04  Miklos Szeredi <mszeredi@inf.bme.hu>
 
        * Fix race between truncate and writepage (fsx-linux now runs
index cb2cea9746443420276634ab1644c450d7f3694a..1af796ba886282c7a5a232785e3f1ad4a578d4b1 100644 (file)
@@ -108,7 +108,8 @@ enum fuse_opcode {
        FUSE_STATFS     = 17,
        FUSE_RELEASE    = 18, /* no reply */
        FUSE_INVALIDATE = 19, /* user initiated */
-       FUSE_FSYNC      = 20
+       FUSE_FSYNC      = 20,
+       FUSE_RELEASE2   = 21  /* reply needed after all */
 };
 
 /* Conservative buffer size for the client */
index daa464e2bfb3a9b2e08a8e31da9cc31d648aa387..f7631086ddf0c8e1e4647cb93c5c0e704b99b82c 100644 (file)
@@ -549,6 +549,7 @@ static struct fuse_conn *new_conn(void)
                fc->file = NULL;
                fc->flags = 0;
                fc->uid = 0;
+               fc->oldrelease = 0;
                init_waitqueue_head(&fc->waitq);
                INIT_LIST_HEAD(&fc->pending);
                INIT_LIST_HEAD(&fc->processing);
index fcb83c51aa2f581c689ba18bb038f8004ed505b7..13c96ae17b2cecb435c8127a40fa948284ed1462 100644 (file)
@@ -71,16 +71,13 @@ void fuse_sync_inode(struct inode *inode)
 #endif
 }
 
-static int fuse_release(struct inode *inode, struct file *file)
+static int fuse_release_old(struct inode *inode, struct file *file)
 {
        struct fuse_conn *fc = INO_FC(inode);
        struct fuse_in *in = NULL;
        struct fuse_open_in *inarg = NULL;
        unsigned int s = sizeof(struct fuse_in) + sizeof(struct fuse_open_in);
 
-       if(file->f_mode & FMODE_WRITE)
-               fuse_sync_inode(inode);
-
        in = kmalloc(s, GFP_NOFS);
        if(!in)
                return -ENOMEM;
@@ -100,6 +97,35 @@ static int fuse_release(struct inode *inode, struct file *file)
        return 0;
 }
 
+static int fuse_release(struct inode *inode, struct file *file)
+{
+       struct fuse_conn *fc = INO_FC(inode);
+       struct fuse_in in = FUSE_IN_INIT;
+       struct fuse_out out = FUSE_OUT_INIT;
+       struct fuse_open_in inarg;
+
+       if(file->f_mode & FMODE_WRITE)
+               fuse_sync_inode(inode);
+
+       if (fc->oldrelease)
+               return fuse_release_old(inode, file);
+
+       memset(&inarg, 0, sizeof(inarg));
+       inarg.flags = file->f_flags & ~O_EXCL;
+
+       in.h.opcode = FUSE_RELEASE2;
+       in.h.ino = inode->i_ino;
+       in.numargs = 1;
+       in.args[0].size = sizeof(inarg);
+       in.args[0].value = &inarg;
+       request_send(fc, &in, &out);
+       if (out.h.error == -ENOSYS) {
+               fc->oldrelease = 1;
+               return fuse_release_old(inode, file);
+       }
+       return 0;
+}
+
 static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
 {
        struct inode *inode = de->d_inode;
index de9f92ac7dff7b4183b78392328bad38b6c3f0b2..0e7d07eddc3bfa84e8c695f95a26a0475a71589d 100644 (file)
@@ -63,6 +63,10 @@ struct fuse_conn {
        /** The fuse mount flags for this mount */
        unsigned int flags;
 
+       /** Is the new (synchronous) release not supported by
+           userspace? */
+       unsigned int oldrelease;
+
        /** Readers of the connection are waiting on this */
        wait_queue_head_t waitq;
 
index 7aa1eca73a6976e856b16edccae49954b08d458b..ad0aaed66eab6e9b7537ee31f80e6e0fe1714c0a 100644 (file)
@@ -41,6 +41,7 @@ static const char *opname(enum fuse_opcode opcode)
     case FUSE_STATFS:   return "STATFS";
     case FUSE_RELEASE:  return "RELEASE";
     case FUSE_FSYNC:    return "FSYNC";
+    case FUSE_RELEASE2: return "RELEASE2";
     default:            return "???";
     }
 }
@@ -765,7 +766,7 @@ static void do_open(struct fuse *f, struct fuse_in_header *in,
 }
 
 static void do_release(struct fuse *f, struct fuse_in_header *in,
-                       struct fuse_open_in *arg)
+                       struct fuse_open_in *arg, int reply)
 {
     char *path;
 
@@ -775,6 +776,8 @@ static void do_release(struct fuse *f, struct fuse_in_header *in,
             f->op.release(path, arg->flags);
         free(path);
     }
+    if (reply)
+        send_reply(f, in, 0, NULL, 0);
 }
 
 static void do_read(struct fuse *f, struct fuse_in_header *in,
@@ -964,7 +967,11 @@ void __fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd)
         break;
 
     case FUSE_RELEASE:
-        do_release(f, in, (struct fuse_open_in *) inarg);
+        do_release(f, in, (struct fuse_open_in *) inarg, 0);
+        break;
+
+    case FUSE_RELEASE2:
+        do_release(f, in, (struct fuse_open_in *) inarg, 1);
         break;
 
     case FUSE_READ: