summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2006-02-17 13:52:52 -0800
committerLinus Torvalds <torvalds@g5.osdl.org>2006-02-17 13:59:27 -0800
commit77e7f250f88cd62844e24c42aff4d0e95969c746 (patch)
treeee14c76d87f8ac141d2fee43e40278b5fcadadd8
parenta8534adb74e23374889b84b3d97eb18da542a1b5 (diff)
downloadlinux-77e7f250f88cd62844e24c42aff4d0e95969c746.tar.gz
linux-77e7f250f88cd62844e24c42aff4d0e95969c746.tar.bz2
linux-77e7f250f88cd62844e24c42aff4d0e95969c746.zip
[PATCH] fuse: fix bug in aborted fuse_release_end()
There's a rather theoretical case of the BUG triggering in fuse_reset_request(): - iget() fails because of OOM after a successful CREATE_OPEN request - during IO on the resulting RELEASE request the connection is aborted Fix and add warning to fuse_reset_request(). Signed-off-by: Miklos Szeredi <miklos@szeredi.hu> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--fs/fuse/dev.c6
-rw-r--r--fs/fuse/file.c11
2 files changed, 14 insertions, 3 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index f556a0d5c0d3..0c9a2ee54c91 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -66,6 +66,12 @@ static void restore_sigs(sigset_t *oldset)
sigprocmask(SIG_SETMASK, oldset, NULL);
}
+/*
+ * Reset request, so that it can be reused
+ *
+ * The caller must be _very_ careful to make sure, that it is holding
+ * the only reference to req
+ */
void fuse_reset_request(struct fuse_req *req)
{
int preallocated = req->preallocated;
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 296351615b00..6f05379b0a0d 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -116,9 +116,14 @@ int fuse_open_common(struct inode *inode, struct file *file, int isdir)
/* Special case for failed iget in CREATE */
static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req)
{
- u64 nodeid = req->in.h.nodeid;
- fuse_reset_request(req);
- fuse_send_forget(fc, req, nodeid, 1);
+ /* If called from end_io_requests(), req has more than one
+ reference and fuse_reset_request() cannot work */
+ if (fc->connected) {
+ u64 nodeid = req->in.h.nodeid;
+ fuse_reset_request(req);
+ fuse_send_forget(fc, req, nodeid, 1);
+ } else
+ fuse_put_request(fc, req);
}
void fuse_send_release(struct fuse_conn *fc, struct fuse_file *ff,