diff options
author | Ian Kent <raven@themaw.net> | 2008-07-24 00:30:26 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-24 13:47:32 -0400 |
commit | 97e7449a7ad883bf9f516fc970778d75999c7843 (patch) | |
tree | 903f6de243847eb12d1a372b271b15046ecdc774 /fs/autofs4/root.c | |
parent | 26e81b3142f1ba497d4cd0365c13661684b784ce (diff) |
autofs4: fix indirect mount pending expire race
The selection of a dentry for expiration and the setting of the
AUTOFS_INF_EXPIRING flag isn't done atomically which can lead to lookups
walking into an expiring mount.
What happens is that an expire is initiated by the daemon and a dentry is
selected for expire but, since there is no lock held between the selection
and setting of the expiring flag, a process may find the flag clear and
continue walking into the mount tree at the same time the daemon attempts
the expire it.
Signed-off-by: Ian Kent <raven@themaw.net>
Reviewed-by: Jeff Moyer <jmoyer@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/autofs4/root.c')
-rw-r--r-- | fs/autofs4/root.c | 32 |
1 files changed, 27 insertions, 5 deletions
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 61d1dca16884..1c2579de1f2e 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c | |||
@@ -133,7 +133,10 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags) | |||
133 | /* Block on any pending expiry here; invalidate the dentry | 133 | /* Block on any pending expiry here; invalidate the dentry |
134 | when expiration is done to trigger mount request with a new | 134 | when expiration is done to trigger mount request with a new |
135 | dentry */ | 135 | dentry */ |
136 | if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { | 136 | spin_lock(&sbi->fs_lock); |
137 | if (ino->flags & AUTOFS_INF_EXPIRING) { | ||
138 | spin_unlock(&sbi->fs_lock); | ||
139 | |||
137 | DPRINTK("waiting for expire %p name=%.*s", | 140 | DPRINTK("waiting for expire %p name=%.*s", |
138 | dentry, dentry->d_name.len, dentry->d_name.name); | 141 | dentry, dentry->d_name.len, dentry->d_name.name); |
139 | 142 | ||
@@ -149,8 +152,11 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags) | |||
149 | status = d_invalidate(dentry); | 152 | status = d_invalidate(dentry); |
150 | if (status != -EBUSY) | 153 | if (status != -EBUSY) |
151 | return -EAGAIN; | 154 | return -EAGAIN; |
152 | } | ||
153 | 155 | ||
156 | goto cont; | ||
157 | } | ||
158 | spin_unlock(&sbi->fs_lock); | ||
159 | cont: | ||
154 | DPRINTK("dentry=%p %.*s ino=%p", | 160 | DPRINTK("dentry=%p %.*s ino=%p", |
155 | dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); | 161 | dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); |
156 | 162 | ||
@@ -229,15 +235,21 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
229 | goto done; | 235 | goto done; |
230 | 236 | ||
231 | /* If an expire request is pending wait for it. */ | 237 | /* If an expire request is pending wait for it. */ |
232 | if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { | 238 | spin_lock(&sbi->fs_lock); |
239 | if (ino->flags & AUTOFS_INF_EXPIRING) { | ||
240 | spin_unlock(&sbi->fs_lock); | ||
241 | |||
233 | DPRINTK("waiting for active request %p name=%.*s", | 242 | DPRINTK("waiting for active request %p name=%.*s", |
234 | dentry, dentry->d_name.len, dentry->d_name.name); | 243 | dentry, dentry->d_name.len, dentry->d_name.name); |
235 | 244 | ||
236 | status = autofs4_wait(sbi, dentry, NFY_NONE); | 245 | status = autofs4_wait(sbi, dentry, NFY_NONE); |
237 | 246 | ||
238 | DPRINTK("request done status=%d", status); | 247 | DPRINTK("request done status=%d", status); |
239 | } | ||
240 | 248 | ||
249 | goto cont; | ||
250 | } | ||
251 | spin_unlock(&sbi->fs_lock); | ||
252 | cont: | ||
241 | /* | 253 | /* |
242 | * If the dentry contains directories then it is an | 254 | * If the dentry contains directories then it is an |
243 | * autofs multi-mount with no root mount offset. So | 255 | * autofs multi-mount with no root mount offset. So |
@@ -292,8 +304,11 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
292 | int status = 1; | 304 | int status = 1; |
293 | 305 | ||
294 | /* Pending dentry */ | 306 | /* Pending dentry */ |
307 | spin_lock(&sbi->fs_lock); | ||
295 | if (autofs4_ispending(dentry)) { | 308 | if (autofs4_ispending(dentry)) { |
296 | /* The daemon never causes a mount to trigger */ | 309 | /* The daemon never causes a mount to trigger */ |
310 | spin_unlock(&sbi->fs_lock); | ||
311 | |||
297 | if (oz_mode) | 312 | if (oz_mode) |
298 | return 1; | 313 | return 1; |
299 | 314 | ||
@@ -316,6 +331,7 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
316 | 331 | ||
317 | return status; | 332 | return status; |
318 | } | 333 | } |
334 | spin_unlock(&sbi->fs_lock); | ||
319 | 335 | ||
320 | /* Negative dentry.. invalidate if "old" */ | 336 | /* Negative dentry.. invalidate if "old" */ |
321 | if (dentry->d_inode == NULL) | 337 | if (dentry->d_inode == NULL) |
@@ -329,6 +345,7 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
329 | DPRINTK("dentry=%p %.*s, emptydir", | 345 | DPRINTK("dentry=%p %.*s, emptydir", |
330 | dentry, dentry->d_name.len, dentry->d_name.name); | 346 | dentry, dentry->d_name.len, dentry->d_name.name); |
331 | spin_unlock(&dcache_lock); | 347 | spin_unlock(&dcache_lock); |
348 | |||
332 | /* The daemon never causes a mount to trigger */ | 349 | /* The daemon never causes a mount to trigger */ |
333 | if (oz_mode) | 350 | if (oz_mode) |
334 | return 1; | 351 | return 1; |
@@ -521,13 +538,18 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s | |||
521 | * so it must have been successful, so just wait for it. | 538 | * so it must have been successful, so just wait for it. |
522 | */ | 539 | */ |
523 | ino = autofs4_dentry_ino(expiring); | 540 | ino = autofs4_dentry_ino(expiring); |
524 | while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { | 541 | spin_lock(&sbi->fs_lock); |
542 | if (ino->flags & AUTOFS_INF_EXPIRING) { | ||
543 | spin_unlock(&sbi->fs_lock); | ||
525 | DPRINTK("wait for incomplete expire %p name=%.*s", | 544 | DPRINTK("wait for incomplete expire %p name=%.*s", |
526 | expiring, expiring->d_name.len, | 545 | expiring, expiring->d_name.len, |
527 | expiring->d_name.name); | 546 | expiring->d_name.name); |
528 | autofs4_wait(sbi, expiring, NFY_NONE); | 547 | autofs4_wait(sbi, expiring, NFY_NONE); |
529 | DPRINTK("request completed"); | 548 | DPRINTK("request completed"); |
549 | goto cont; | ||
530 | } | 550 | } |
551 | spin_unlock(&sbi->fs_lock); | ||
552 | cont: | ||
531 | spin_lock(&sbi->lookup_lock); | 553 | spin_lock(&sbi->lookup_lock); |
532 | if (!list_empty(&ino->expiring)) | 554 | if (!list_empty(&ino->expiring)) |
533 | list_del_init(&ino->expiring); | 555 | list_del_init(&ino->expiring); |