From 3743f3fe52cfd52bb1d75bcb8324a00f153dc9f8 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 9 Dec 2011 16:07:55 +0100 Subject: 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. --- ChangeLog | 4 ++++ lib/fuse.c | 38 ++++++++++++++++++++++++++++++++++---- 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 + + * Fix hang in wait_on_path(). Reported by Ville Silventoinen + 2011-10-13 Miklos Szeredi * 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; } -- cgit v1.2.1