Fix hang in wait_on_path()
authorMiklos Szeredi <mszeredi@suse.cz>
Fri, 9 Dec 2011 15:07:55 +0000 (16:07 +0100)
committerMiklos Szeredi <mszeredi@suse.cz>
Fri, 9 Dec 2011 15:18:05 +0000 (16:18 +0100)
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
lib/fuse.c

index 77a9b9ef7e21286f818771618bd14e64f6ade3a3..2c25c0bc1178afeb68872c56b0134ce5311c0e5c 100644 (file)
--- 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
index 95cf50bad19aa6bc9bfc83777052ec646c6155ac..d511964456d536a150f1553732642eacfb6eaeb0 100644 (file)
@@ -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;
 }