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/expire.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/expire.c')
-rw-r--r-- | fs/autofs4/expire.c | 46 |
1 files changed, 35 insertions, 11 deletions
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index 894fee54d4d8..19f5bea2704f 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c | |||
@@ -292,6 +292,8 @@ static struct dentry *autofs4_expire_indirect(struct super_block *sb, | |||
292 | struct list_head *next; | 292 | struct list_head *next; |
293 | int do_now = how & AUTOFS_EXP_IMMEDIATE; | 293 | int do_now = how & AUTOFS_EXP_IMMEDIATE; |
294 | int exp_leaves = how & AUTOFS_EXP_LEAVES; | 294 | int exp_leaves = how & AUTOFS_EXP_LEAVES; |
295 | struct autofs_info *ino; | ||
296 | unsigned int ino_count; | ||
295 | 297 | ||
296 | if (!root) | 298 | if (!root) |
297 | return NULL; | 299 | return NULL; |
@@ -316,6 +318,9 @@ static struct dentry *autofs4_expire_indirect(struct super_block *sb, | |||
316 | dentry = dget(dentry); | 318 | dentry = dget(dentry); |
317 | spin_unlock(&dcache_lock); | 319 | spin_unlock(&dcache_lock); |
318 | 320 | ||
321 | spin_lock(&sbi->fs_lock); | ||
322 | ino = autofs4_dentry_ino(dentry); | ||
323 | |||
319 | /* | 324 | /* |
320 | * Case 1: (i) indirect mount or top level pseudo direct mount | 325 | * Case 1: (i) indirect mount or top level pseudo direct mount |
321 | * (autofs-4.1). | 326 | * (autofs-4.1). |
@@ -326,6 +331,11 @@ static struct dentry *autofs4_expire_indirect(struct super_block *sb, | |||
326 | DPRINTK("checking mountpoint %p %.*s", | 331 | DPRINTK("checking mountpoint %p %.*s", |
327 | dentry, (int)dentry->d_name.len, dentry->d_name.name); | 332 | dentry, (int)dentry->d_name.len, dentry->d_name.name); |
328 | 333 | ||
334 | /* Path walk currently on this dentry? */ | ||
335 | ino_count = atomic_read(&ino->count) + 2; | ||
336 | if (atomic_read(&dentry->d_count) > ino_count) | ||
337 | goto next; | ||
338 | |||
329 | /* Can we umount this guy */ | 339 | /* Can we umount this guy */ |
330 | if (autofs4_mount_busy(mnt, dentry)) | 340 | if (autofs4_mount_busy(mnt, dentry)) |
331 | goto next; | 341 | goto next; |
@@ -343,23 +353,25 @@ static struct dentry *autofs4_expire_indirect(struct super_block *sb, | |||
343 | 353 | ||
344 | /* Case 2: tree mount, expire iff entire tree is not busy */ | 354 | /* Case 2: tree mount, expire iff entire tree is not busy */ |
345 | if (!exp_leaves) { | 355 | if (!exp_leaves) { |
346 | /* Lock the tree as we must expire as a whole */ | 356 | /* Path walk currently on this dentry? */ |
347 | spin_lock(&sbi->fs_lock); | 357 | ino_count = atomic_read(&ino->count) + 1; |
348 | if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { | 358 | if (atomic_read(&dentry->d_count) > ino_count) |
349 | struct autofs_info *inf = autofs4_dentry_ino(dentry); | 359 | goto next; |
350 | 360 | ||
351 | /* Set this flag early to catch sys_chdir and the like */ | 361 | if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { |
352 | inf->flags |= AUTOFS_INF_EXPIRING; | ||
353 | spin_unlock(&sbi->fs_lock); | ||
354 | expired = dentry; | 362 | expired = dentry; |
355 | goto found; | 363 | goto found; |
356 | } | 364 | } |
357 | spin_unlock(&sbi->fs_lock); | ||
358 | /* | 365 | /* |
359 | * Case 3: pseudo direct mount, expire individual leaves | 366 | * Case 3: pseudo direct mount, expire individual leaves |
360 | * (autofs-4.1). | 367 | * (autofs-4.1). |
361 | */ | 368 | */ |
362 | } else { | 369 | } else { |
370 | /* Path walk currently on this dentry? */ | ||
371 | ino_count = atomic_read(&ino->count) + 1; | ||
372 | if (atomic_read(&dentry->d_count) > ino_count) | ||
373 | goto next; | ||
374 | |||
363 | expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); | 375 | expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); |
364 | if (expired) { | 376 | if (expired) { |
365 | dput(dentry); | 377 | dput(dentry); |
@@ -367,6 +379,7 @@ static struct dentry *autofs4_expire_indirect(struct super_block *sb, | |||
367 | } | 379 | } |
368 | } | 380 | } |
369 | next: | 381 | next: |
382 | spin_unlock(&sbi->fs_lock); | ||
370 | dput(dentry); | 383 | dput(dentry); |
371 | spin_lock(&dcache_lock); | 384 | spin_lock(&dcache_lock); |
372 | next = next->next; | 385 | next = next->next; |
@@ -377,6 +390,9 @@ next: | |||
377 | found: | 390 | found: |
378 | DPRINTK("returning %p %.*s", | 391 | DPRINTK("returning %p %.*s", |
379 | expired, (int)expired->d_name.len, expired->d_name.name); | 392 | expired, (int)expired->d_name.len, expired->d_name.name); |
393 | ino = autofs4_dentry_ino(expired); | ||
394 | ino->flags |= AUTOFS_INF_EXPIRING; | ||
395 | spin_unlock(&sbi->fs_lock); | ||
380 | spin_lock(&dcache_lock); | 396 | spin_lock(&dcache_lock); |
381 | list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); | 397 | list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); |
382 | spin_unlock(&dcache_lock); | 398 | spin_unlock(&dcache_lock); |
@@ -390,7 +406,9 @@ int autofs4_expire_run(struct super_block *sb, | |||
390 | struct autofs_packet_expire __user *pkt_p) | 406 | struct autofs_packet_expire __user *pkt_p) |
391 | { | 407 | { |
392 | struct autofs_packet_expire pkt; | 408 | struct autofs_packet_expire pkt; |
409 | struct autofs_info *ino; | ||
393 | struct dentry *dentry; | 410 | struct dentry *dentry; |
411 | int ret = 0; | ||
394 | 412 | ||
395 | memset(&pkt,0,sizeof pkt); | 413 | memset(&pkt,0,sizeof pkt); |
396 | 414 | ||
@@ -406,9 +424,14 @@ int autofs4_expire_run(struct super_block *sb, | |||
406 | dput(dentry); | 424 | dput(dentry); |
407 | 425 | ||
408 | if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) | 426 | if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) |
409 | return -EFAULT; | 427 | ret = -EFAULT; |
410 | 428 | ||
411 | return 0; | 429 | spin_lock(&sbi->fs_lock); |
430 | ino = autofs4_dentry_ino(dentry); | ||
431 | ino->flags &= ~AUTOFS_INF_EXPIRING; | ||
432 | spin_unlock(&sbi->fs_lock); | ||
433 | |||
434 | return ret; | ||
412 | } | 435 | } |
413 | 436 | ||
414 | /* Call repeatedly until it returns -EAGAIN, meaning there's nothing | 437 | /* Call repeatedly until it returns -EAGAIN, meaning there's nothing |
@@ -433,9 +456,10 @@ int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, | |||
433 | 456 | ||
434 | /* This is synchronous because it makes the daemon a | 457 | /* This is synchronous because it makes the daemon a |
435 | little easier */ | 458 | little easier */ |
436 | ino->flags |= AUTOFS_INF_EXPIRING; | ||
437 | ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); | 459 | ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); |
460 | spin_lock(&sbi->fs_lock); | ||
438 | ino->flags &= ~AUTOFS_INF_EXPIRING; | 461 | ino->flags &= ~AUTOFS_INF_EXPIRING; |
462 | spin_unlock(&sbi->fs_lock); | ||
439 | dput(dentry); | 463 | dput(dentry); |
440 | } | 464 | } |
441 | 465 | ||