[PATCH] autofs4: tree race fix
authorIan Kent <raven@themaw.net>
Sun, 1 May 2005 15:59:17 +0000 (08:59 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Sun, 1 May 2005 15:59:17 +0000 (08:59 -0700)
For tree mount maps, a call to chdir or chroot, to a directory above the
moint point directories at a certain time during the expire results in the
expire incorrectly thinking the tree is not busy.  This patch adds a check
to see if the filesystem above the tree mount points is busy and also locks
the filesystem during the tree mount expire to prevent the race.

Signed-off-by: Ian Kent <raven@themaw.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/autofs4/autofs_i.h
fs/autofs4/expire.c
fs/autofs4/inode.c

index 9789877352522440d723186002c32d8ddc6d5d20..c7b2b8890188599e09f19c255fa9ec1014342e2e 100644 (file)
@@ -102,6 +102,7 @@ struct autofs_sb_info {
        int needs_reghost;
        struct super_block *sb;
        struct semaphore wq_sem;
+       spinlock_t fs_lock;
        struct autofs_wait_queue *queues; /* Wait queue pointer */
 };
 
@@ -127,9 +128,18 @@ static inline int autofs4_oz_mode(struct autofs_sb_info *sbi) {
 static inline int autofs4_ispending(struct dentry *dentry)
 {
        struct autofs_info *inf = autofs4_dentry_ino(dentry);
+       int pending = 0;
 
-       return (dentry->d_flags & DCACHE_AUTOFS_PENDING) ||
-               (inf != NULL && inf->flags & AUTOFS_INF_EXPIRING);
+       if (dentry->d_flags & DCACHE_AUTOFS_PENDING)
+               return 1;
+
+       if (inf) {
+               spin_lock(&inf->sbi->fs_lock);
+               pending = inf->flags & AUTOFS_INF_EXPIRING;
+               spin_unlock(&inf->sbi->fs_lock);
+       }
+
+       return pending;
 }
 
 static inline void autofs4_copy_atime(struct file *src, struct file *dst)
index 31540a6404d91ef6c2373a7bb0091af3ef1a8c99..500425e24fba9178c13db5550a7f9271256249d4 100644 (file)
@@ -99,6 +99,10 @@ static int autofs4_check_tree(struct vfsmount *mnt,
        if (!autofs4_can_expire(top, timeout, do_now))
                return 0;
 
+       /* Is someone visiting anywhere in the tree ? */
+       if (may_umount_tree(mnt))
+               return 0;
+
        spin_lock(&dcache_lock);
 repeat:
        next = this_parent->d_subdirs.next;
@@ -270,10 +274,18 @@ static struct dentry *autofs4_expire(struct super_block *sb,
 
                /* Case 2: tree mount, expire iff entire tree is not busy */
                if (!exp_leaves) {
+                       /* Lock the tree as we must expire as a whole */
+                       spin_lock(&sbi->fs_lock);
                        if (autofs4_check_tree(mnt, dentry, timeout, do_now)) {
-                       expired = dentry;
-                       break;
+                               struct autofs_info *inf = autofs4_dentry_ino(dentry);
+
+                               /* Set this flag early to catch sys_chdir and the like */
+                               inf->flags |= AUTOFS_INF_EXPIRING;
+                               spin_unlock(&sbi->fs_lock);
+                               expired = dentry;
+                               break;
                        }
+                       spin_unlock(&sbi->fs_lock);
                /* Case 3: direct mount, expire individual leaves */
                } else {
                        expired = autofs4_check_leaves(mnt, dentry, timeout, do_now);
index a52560746628b4cc7020c80fced225b9ddf96109..4bb14cc680407eb808d4b2a9876a54708b639224 100644 (file)
@@ -206,6 +206,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
        sbi->version = 0;
        sbi->sub_version = 0;
        init_MUTEX(&sbi->wq_sem);
+       spin_lock_init(&sbi->fs_lock);
        sbi->queues = NULL;
        s->s_blocksize = 1024;
        s->s_blocksize_bits = 10;