summaryrefslogtreecommitdiffstats
path: root/fs/autofs4/expire.c
diff options
context:
space:
mode:
authorIan Kent <raven@themaw.net>2008-07-23 21:30:27 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2008-07-24 10:47:32 -0700
commit6e60a9ab5f5d314735467752f623072f5b75157a (patch)
tree1a42df40e6fad52bd6be74970af8eb56870fe6ba /fs/autofs4/expire.c
parent97e7449a7ad883bf9f516fc970778d75999c7843 (diff)
downloadlinux-stable-6e60a9ab5f5d314735467752f623072f5b75157a.tar.gz
linux-stable-6e60a9ab5f5d314735467752f623072f5b75157a.tar.bz2
linux-stable-6e60a9ab5f5d314735467752f623072f5b75157a.zip
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/expire.c')
-rw-r--r--fs/autofs4/expire.c16
1 files changed, 13 insertions, 3 deletions
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c
index 19f5bea2704f..705b9f057fb3 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,
now = jiffies;
timeout = sbi->exp_timeout;
- /* Lock the tree as we must expire as a whole */
spin_lock(&sbi->fs_lock);
if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
struct autofs_info *ino = autofs4_dentry_ino(root);
-
- /* Set this flag early to catch sys_chdir and the like */
+ if (d_mountpoint(root)) {
+ ino->flags |= AUTOFS_INF_MOUNTPOINT;
+ root->d_mounted--;
+ }
ino->flags |= AUTOFS_INF_EXPIRING;
+ init_completion(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
return root;
}
@@ -392,6 +394,7 @@ found:
expired, (int)expired->d_name.len, expired->d_name.name);
ino = autofs4_dentry_ino(expired);
ino->flags |= AUTOFS_INF_EXPIRING;
+ init_completion(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
spin_lock(&dcache_lock);
list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child);
@@ -429,6 +432,7 @@ int autofs4_expire_run(struct super_block *sb,
spin_lock(&sbi->fs_lock);
ino = autofs4_dentry_ino(dentry);
ino->flags &= ~AUTOFS_INF_EXPIRING;
+ complete_all(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
return ret;
@@ -457,8 +461,14 @@ int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt,
/* This is synchronous because it makes the daemon a
little easier */
ret = autofs4_wait(sbi, dentry, NFY_EXPIRE);
+
spin_lock(&sbi->fs_lock);
+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) {
+ sb->s_root->d_mounted++;
+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT;
+ }
ino->flags &= ~AUTOFS_INF_EXPIRING;
+ complete_all(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
dput(dentry);
}