summaryrefslogtreecommitdiff
path: root/trans
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2014-02-07 22:54:24 +0100
committerRichard Braun <rbraun@sceen.net>2014-02-07 22:54:24 +0100
commit1ea83c98a8fb8f58a4de8368d1fa29d341a2a73c (patch)
tree2ac86e477eec373b6566c82e19537d3b50ba1073 /trans
parenteafc1c8d2223693e96c9619897f92774735fcb49 (diff)
trans/fakeroot: fix cached node retrieval on lookup
When a client finds a node from the hash table, it could happen that another thread is still holding one reference on it before the current thread has acquired its own. Simply checking for a non zero refcount isn't enough, the new client must atomically acquire its own reference. * trans/fakeroot.c (netfs_S_dir_lookup): Find and acquire node reference while holding netfs_node_refcnt_lock.
Diffstat (limited to 'trans')
-rw-r--r--trans/fakeroot.c11
1 files changed, 10 insertions, 1 deletions
diff --git a/trans/fakeroot.c b/trans/fakeroot.c
index 30386126..c519180c 100644
--- a/trans/fakeroot.c
+++ b/trans/fakeroot.c
@@ -348,6 +348,10 @@ netfs_S_dir_lookup (struct protid *diruser,
redo_hash_lookup:
pthread_mutex_lock (&idport_ihash_lock);
pthread_mutex_lock (&dnp->lock);
+ /* The hashtable may not hold a true reference on the node. Acquire the
+ refcount lock so that, if a node is found, its reference counter cannot
+ drop to 0 before we get our own reference. */
+ pthread_spin_lock (&netfs_node_refcnt_lock);
struct netnode *nn = hurd_ihash_find (&idport_ihash, idport);
if (nn != NULL)
{
@@ -359,12 +363,17 @@ netfs_S_dir_lookup (struct protid *diruser,
/* But it might be in the process of being released. If so,
unlock the hash table to give the node a chance to actually
be removed and retry. */
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
pthread_mutex_unlock (&dnp->lock);
pthread_mutex_unlock (&idport_ihash_lock);
goto redo_hash_lookup;
}
+ /* Otherwise, reference it right away. */
np = nn->np;
+ np->references++;
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+
mach_port_deallocate (mach_task_self (), idport);
if (np == dnp)
@@ -377,12 +386,12 @@ netfs_S_dir_lookup (struct protid *diruser,
pthread_mutex_unlock (&dnp->lock);
}
- netfs_nref (np);
err = check_openmodes (np->nn, (flags & (O_RDWR|O_EXEC)), file);
pthread_mutex_unlock (&idport_ihash_lock);
}
else
{
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
err = new_node (file, idport, 1, flags, &np);
pthread_mutex_unlock (&dnp->lock);
if (!err)