From 5fc32d3233f571fa642bf2d694c9a652ac64e1df Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 6 Jul 2004 13:16:30 +0000 Subject: [PATCH] fix release/forget race --- ChangeLog | 6 ++++++ include/linux/fuse.h | 3 ++- kernel/dev.c | 1 + kernel/file.c | 34 ++++++++++++++++++++++++++++++---- kernel/fuse_i.h | 4 ++++ lib/fuse.c | 11 +++++++++-- 6 files changed, 52 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index f38ce27..b3676b8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2004-07-06 Miklos Szeredi + + * 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 * Fix race between truncate and writepage (fsx-linux now runs diff --git a/include/linux/fuse.h b/include/linux/fuse.h index cb2cea9..1af796b 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h @@ -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 */ diff --git a/kernel/dev.c b/kernel/dev.c index daa464e..f763108 100644 --- a/kernel/dev.c +++ b/kernel/dev.c @@ -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); diff --git a/kernel/file.c b/kernel/file.c index fcb83c5..13c96ae 100644 --- a/kernel/file.c +++ b/kernel/file.c @@ -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; diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h index de9f92a..0e7d07e 100644 --- a/kernel/fuse_i.h +++ b/kernel/fuse_i.h @@ -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; diff --git a/lib/fuse.c b/lib/fuse.c index 7aa1eca..ad0aaed 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -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: -- 2.30.2