summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2011-12-09 16:07:55 +0100
committerMiklos Szeredi <mszeredi@suse.cz>2011-12-09 16:18:05 +0100
commit3743f3fe52cfd52bb1d75bcb8324a00f153dc9f8 (patch)
tree04062cb279e8e7a069d879e40ac9966499e472ac
parent655794f4a1515f1eb5b00b2fcc32d3f31f56839b (diff)
downloadfuse-3743f3fe52cfd52bb1d75bcb8324a00f153dc9f8.tar.gz
Fix hang in wait_on_path()
Ville Silventoinen reported that fs_racer in LTP triggered a hang in wait_on_path(). This bug was caused by try_get_path() not resetting "ticket" on permanent failure.
-rw-r--r--ChangeLog4
-rw-r--r--lib/fuse.c38
2 files changed, 38 insertions, 4 deletions
diff --git a/ChangeLog b/ChangeLog
index 77a9b9e..2c25c0b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2011-12-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix hang in wait_on_path(). Reported by Ville Silventoinen
+
2011-10-13 Miklos Szeredi <miklos@szeredi.hu>
* Reply to request with ENOMEM in case of failure to allocate
diff --git a/lib/fuse.c b/lib/fuse.c
index 95cf50b..d511964 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -495,6 +495,26 @@ static void unlock_path(struct fuse *f, fuse_ino_t nodeid, struct node *wnode,
}
}
+static void release_tickets(struct fuse *f, fuse_ino_t nodeid,
+ struct node *wnode, int ticket)
+{
+ struct node *node;
+
+ if (wnode) {
+ if (wnode->ticket != ticket)
+ return;
+
+ wnode->ticket = 0;
+ }
+
+ for (node = get_node(f, nodeid);
+ node->nodeid != FUSE_ROOT_ID; node = node->parent) {
+ if (node->ticket != ticket)
+ return;
+ node->ticket = 0;
+ }
+}
+
static int try_get_path(struct fuse *f, fuse_ino_t nodeid, const char *name,
char **path, struct node **wnodep, int ticket)
{
@@ -507,9 +527,10 @@ static int try_get_path(struct fuse *f, fuse_ino_t nodeid, const char *name,
*path = NULL;
+ err = -ENOMEM;
buf = malloc(bufsize);
if (buf == NULL)
- return -ENOMEM;
+ goto out_err;
s = buf + bufsize - 1;
*s = '\0';
@@ -577,6 +598,10 @@ static int try_get_path(struct fuse *f, fuse_ino_t nodeid, const char *name,
out_free:
free(buf);
+ out_err:
+ if (ticket && err != -EAGAIN)
+ release_tickets(f, nodeid, wnode, ticket);
+
return err;
}
@@ -711,9 +736,14 @@ static int try_get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
err = try_get_path(f, nodeid1, name1, path1, wnode1, ticket);
if (!err) {
err = try_get_path(f, nodeid2, name2, path2, wnode2, ticket);
- if (err)
- unlock_path(f, nodeid1, wnode1 ? *wnode1 : NULL, NULL,
- ticket);
+ if (err) {
+ struct node *wn1 = wnode1 ? *wnode1 : NULL;
+
+ unlock_path(f, nodeid1, wn1, NULL, ticket);
+ free(path1);
+ if (ticket && err != -EAGAIN)
+ release_tickets(f, nodeid1, wn1, ticket);
+ }
}
return err;
}