diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2011-12-09 16:07:55 +0100 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2011-12-09 16:18:05 +0100 |
commit | 3743f3fe52cfd52bb1d75bcb8324a00f153dc9f8 (patch) | |
tree | 04062cb279e8e7a069d879e40ac9966499e472ac | |
parent | 655794f4a1515f1eb5b00b2fcc32d3f31f56839b (diff) | |
download | fuse-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-- | ChangeLog | 4 | ||||
-rw-r--r-- | lib/fuse.c | 38 |
2 files changed, 38 insertions, 4 deletions
@@ -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 @@ -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; } |