diff options
author | Ian Kent <raven@themaw.net> | 2008-07-24 00:30:27 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-24 13:47:32 -0400 |
commit | 6e60a9ab5f5d314735467752f623072f5b75157a (patch) | |
tree | 1a42df40e6fad52bd6be74970af8eb56870fe6ba /fs/autofs4 | |
parent | 97e7449a7ad883bf9f516fc970778d75999c7843 (diff) |
autofs4: fix direct mount pending expire race
For direct and offset type mounts that are covered by another mount we
cannot check the AUTOFS_INF_EXPIRING flag during a path walk which leads
to lookups walking into an expiring mount while it is being expired.
For example, for the direct multi-mount map entry with a couple of
offsets:
/race/mm1 / <server1>:/<path1>
/om1 <server2>:/<path2>
/om2 <server1>:/<path3>
an autofs trigger mount is mounted on /race/mm1 and when accessed it is
over mounted and trigger mounts made for /race/mm1/om1 and /race/mm1/om2.
So it isn't possible for path walks to see the expiring flag at all and
they happily walk into the file system while it is expiring.
When expiring these mounts follow_down() must stop at the autofs mount and
all processes must block in the ->follow_link() method (except the daemon)
until the expire is complete. This is done by decrementing the d_mounted
field of the autofs trigger mount root dentry until the expire is
completed. In ->follow_link() all processes wait on the expire and the
mount following is completed for the daemon until the expire is complete.
Signed-off-by: Ian Kent <raven@themaw.net>
Cc: 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')
-rw-r--r-- | fs/autofs4/autofs_i.h | 3 | ||||
-rw-r--r-- | fs/autofs4/expire.c | 16 | ||||
-rw-r--r-- | fs/autofs4/root.c | 72 |
3 files changed, 65 insertions, 26 deletions
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 5d90ed3b4b4..4b40cbc71e9 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h | |||
@@ -52,6 +52,8 @@ struct autofs_info { | |||
52 | 52 | ||
53 | int flags; | 53 | int flags; |
54 | 54 | ||
55 | struct completion expire_complete; | ||
56 | |||
55 | struct list_head active; | 57 | struct list_head active; |
56 | struct list_head expiring; | 58 | struct list_head expiring; |
57 | 59 | ||
@@ -69,6 +71,7 @@ struct autofs_info { | |||
69 | }; | 71 | }; |
70 | 72 | ||
71 | #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ | 73 | #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ |
74 | #define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ | ||
72 | 75 | ||
73 | struct autofs_wait_queue { | 76 | struct autofs_wait_queue { |
74 | wait_queue_head_t queue; | 77 | wait_queue_head_t queue; |
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index 19f5bea2704..705b9f057fb 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c | |||
@@ -259,13 +259,15 @@ static struct dentry *autofs4_expire_direct(struct super_block *sb, | |||
259 | now = jiffies; | 259 | now = jiffies; |
260 | timeout = sbi->exp_timeout; | 260 | timeout = sbi->exp_timeout; |
261 | 261 | ||
262 | /* Lock the tree as we must expire as a whole */ | ||
263 | spin_lock(&sbi->fs_lock); | 262 | spin_lock(&sbi->fs_lock); |
264 | if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { | 263 | if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { |
265 | struct autofs_info *ino = autofs4_dentry_ino(root); | 264 | struct autofs_info *ino = autofs4_dentry_ino(root); |
266 | 265 | if (d_mountpoint(root)) { | |
267 | /* Set this flag early to catch sys_chdir and the like */ | 266 | ino->flags |= AUTOFS_INF_MOUNTPOINT; |
267 | root->d_mounted--; | ||
268 | } | ||
268 | ino->flags |= AUTOFS_INF_EXPIRING; | 269 | ino->flags |= AUTOFS_INF_EXPIRING; |
270 | init_completion(&ino->expire_complete); | ||
269 | spin_unlock(&sbi->fs_lock); | 271 | spin_unlock(&sbi->fs_lock); |
270 | return root; | 272 | return root; |
271 | } | 273 | } |
@@ -392,6 +394,7 @@ found: | |||
392 | expired, (int)expired->d_name.len, expired->d_name.name); | 394 | expired, (int)expired->d_name.len, expired->d_name.name); |
393 | ino = autofs4_dentry_ino(expired); | 395 | ino = autofs4_dentry_ino(expired); |
394 | ino->flags |= AUTOFS_INF_EXPIRING; | 396 | ino->flags |= AUTOFS_INF_EXPIRING; |
397 | init_completion(&ino->expire_complete); | ||
395 | spin_unlock(&sbi->fs_lock); | 398 | spin_unlock(&sbi->fs_lock); |
396 | spin_lock(&dcache_lock); | 399 | spin_lock(&dcache_lock); |
397 | list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); | 400 | list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); |
@@ -429,6 +432,7 @@ int autofs4_expire_run(struct super_block *sb, | |||
429 | spin_lock(&sbi->fs_lock); | 432 | spin_lock(&sbi->fs_lock); |
430 | ino = autofs4_dentry_ino(dentry); | 433 | ino = autofs4_dentry_ino(dentry); |
431 | ino->flags &= ~AUTOFS_INF_EXPIRING; | 434 | ino->flags &= ~AUTOFS_INF_EXPIRING; |
435 | complete_all(&ino->expire_complete); | ||
432 | spin_unlock(&sbi->fs_lock); | 436 | spin_unlock(&sbi->fs_lock); |
433 | 437 | ||
434 | return ret; | 438 | return ret; |
@@ -457,8 +461,14 @@ int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, | |||
457 | /* This is synchronous because it makes the daemon a | 461 | /* This is synchronous because it makes the daemon a |
458 | little easier */ | 462 | little easier */ |
459 | ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); | 463 | ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); |
464 | |||
460 | spin_lock(&sbi->fs_lock); | 465 | spin_lock(&sbi->fs_lock); |
466 | if (ino->flags & AUTOFS_INF_MOUNTPOINT) { | ||
467 | sb->s_root->d_mounted++; | ||
468 | ino->flags &= ~AUTOFS_INF_MOUNTPOINT; | ||
469 | } | ||
461 | ino->flags &= ~AUTOFS_INF_EXPIRING; | 470 | ino->flags &= ~AUTOFS_INF_EXPIRING; |
471 | complete_all(&ino->expire_complete); | ||
462 | spin_unlock(&sbi->fs_lock); | 472 | spin_unlock(&sbi->fs_lock); |
463 | dput(dentry); | 473 | dput(dentry); |
464 | } | 474 | } |
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 1c2579de1f2..adbd8559e87 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c | |||
@@ -141,6 +141,7 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags) | |||
141 | dentry, dentry->d_name.len, dentry->d_name.name); | 141 | dentry, dentry->d_name.len, dentry->d_name.name); |
142 | 142 | ||
143 | status = autofs4_wait(sbi, dentry, NFY_NONE); | 143 | status = autofs4_wait(sbi, dentry, NFY_NONE); |
144 | wait_for_completion(&ino->expire_complete); | ||
144 | 145 | ||
145 | DPRINTK("expire done status=%d", status); | 146 | DPRINTK("expire done status=%d", status); |
146 | 147 | ||
@@ -227,14 +228,32 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
227 | DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", | 228 | DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", |
228 | dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, | 229 | dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, |
229 | nd->flags); | 230 | nd->flags); |
230 | 231 | /* | |
231 | /* If it's our master or we shouldn't trigger a mount we're done */ | 232 | * For an expire of a covered direct or offset mount we need |
232 | lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); | 233 | * to beeak out of follow_down() at the autofs mount trigger |
233 | if (oz_mode || | 234 | * (d_mounted--), so we can see the expiring flag, and manage |
234 | !(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) | 235 | * the blocking and following here until the expire is completed. |
236 | */ | ||
237 | if (oz_mode) { | ||
238 | spin_lock(&sbi->fs_lock); | ||
239 | if (ino->flags & AUTOFS_INF_EXPIRING) { | ||
240 | spin_unlock(&sbi->fs_lock); | ||
241 | /* Follow down to our covering mount. */ | ||
242 | if (!follow_down(&nd->path.mnt, &nd->path.dentry)) | ||
243 | goto done; | ||
244 | /* | ||
245 | * We shouldn't need to do this but we have no way | ||
246 | * of knowing what may have been done so try a follow | ||
247 | * just in case. | ||
248 | */ | ||
249 | autofs4_follow_mount(&nd->path.mnt, &nd->path.dentry); | ||
250 | goto done; | ||
251 | } | ||
252 | spin_unlock(&sbi->fs_lock); | ||
235 | goto done; | 253 | goto done; |
254 | } | ||
236 | 255 | ||
237 | /* If an expire request is pending wait for it. */ | 256 | /* If an expire request is pending everyone must wait. */ |
238 | spin_lock(&sbi->fs_lock); | 257 | spin_lock(&sbi->fs_lock); |
239 | if (ino->flags & AUTOFS_INF_EXPIRING) { | 258 | if (ino->flags & AUTOFS_INF_EXPIRING) { |
240 | spin_unlock(&sbi->fs_lock); | 259 | spin_unlock(&sbi->fs_lock); |
@@ -243,6 +262,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
243 | dentry, dentry->d_name.len, dentry->d_name.name); | 262 | dentry, dentry->d_name.len, dentry->d_name.name); |
244 | 263 | ||
245 | status = autofs4_wait(sbi, dentry, NFY_NONE); | 264 | status = autofs4_wait(sbi, dentry, NFY_NONE); |
265 | wait_for_completion(&ino->expire_complete); | ||
246 | 266 | ||
247 | DPRINTK("request done status=%d", status); | 267 | DPRINTK("request done status=%d", status); |
248 | 268 | ||
@@ -250,10 +270,15 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
250 | } | 270 | } |
251 | spin_unlock(&sbi->fs_lock); | 271 | spin_unlock(&sbi->fs_lock); |
252 | cont: | 272 | cont: |
273 | /* We trigger a mount for almost all flags */ | ||
274 | lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); | ||
275 | if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) | ||
276 | goto done; | ||
277 | |||
253 | /* | 278 | /* |
254 | * If the dentry contains directories then it is an | 279 | * If the dentry contains directories then it is an autofs |
255 | * autofs multi-mount with no root mount offset. So | 280 | * multi-mount with no root mount offset. So don't try to |
256 | * don't try to mount it again. | 281 | * mount it again. |
257 | */ | 282 | */ |
258 | spin_lock(&dcache_lock); | 283 | spin_lock(&dcache_lock); |
259 | if (dentry->d_flags & DCACHE_AUTOFS_PENDING || | 284 | if (dentry->d_flags & DCACHE_AUTOFS_PENDING || |
@@ -264,22 +289,22 @@ cont: | |||
264 | if (status) | 289 | if (status) |
265 | goto out_error; | 290 | goto out_error; |
266 | 291 | ||
267 | /* | 292 | goto follow; |
268 | * The mount succeeded but if there is no root mount | ||
269 | * it must be an autofs multi-mount with no root offset | ||
270 | * so we don't need to follow the mount. | ||
271 | */ | ||
272 | if (d_mountpoint(dentry)) { | ||
273 | if (!autofs4_follow_mount(&nd->path.mnt, | ||
274 | &nd->path.dentry)) { | ||
275 | status = -ENOENT; | ||
276 | goto out_error; | ||
277 | } | ||
278 | } | ||
279 | |||
280 | goto done; | ||
281 | } | 293 | } |
282 | spin_unlock(&dcache_lock); | 294 | spin_unlock(&dcache_lock); |
295 | follow: | ||
296 | /* | ||
297 | * If there is no root mount it must be an autofs | ||
298 | * multi-mount with no root offset so we don't need | ||
299 | * to follow it. | ||
300 | */ | ||
301 | if (d_mountpoint(dentry)) { | ||
302 | if (!autofs4_follow_mount(&nd->path.mnt, | ||
303 | &nd->path.dentry)) { | ||
304 | status = -ENOENT; | ||
305 | goto out_error; | ||
306 | } | ||
307 | } | ||
283 | 308 | ||
284 | done: | 309 | done: |
285 | return NULL; | 310 | return NULL; |
@@ -545,6 +570,7 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s | |||
545 | expiring, expiring->d_name.len, | 570 | expiring, expiring->d_name.len, |
546 | expiring->d_name.name); | 571 | expiring->d_name.name); |
547 | autofs4_wait(sbi, expiring, NFY_NONE); | 572 | autofs4_wait(sbi, expiring, NFY_NONE); |
573 | wait_for_completion(&ino->expire_complete); | ||
548 | DPRINTK("request completed"); | 574 | DPRINTK("request completed"); |
549 | goto cont; | 575 | goto cont; |
550 | } | 576 | } |