Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] autofs4: tree race fix

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>

authored by

Ian Kent and committed by
Linus Torvalds
3a9720ce 4dcd00b1

+27 -4
+12 -2
fs/autofs4/autofs_i.h
··· 102 102 int needs_reghost; 103 103 struct super_block *sb; 104 104 struct semaphore wq_sem; 105 + spinlock_t fs_lock; 105 106 struct autofs_wait_queue *queues; /* Wait queue pointer */ 106 107 }; 107 108 ··· 128 127 static inline int autofs4_ispending(struct dentry *dentry) 129 128 { 130 129 struct autofs_info *inf = autofs4_dentry_ino(dentry); 130 + int pending = 0; 131 131 132 - return (dentry->d_flags & DCACHE_AUTOFS_PENDING) || 133 - (inf != NULL && inf->flags & AUTOFS_INF_EXPIRING); 132 + if (dentry->d_flags & DCACHE_AUTOFS_PENDING) 133 + return 1; 134 + 135 + if (inf) { 136 + spin_lock(&inf->sbi->fs_lock); 137 + pending = inf->flags & AUTOFS_INF_EXPIRING; 138 + spin_unlock(&inf->sbi->fs_lock); 139 + } 140 + 141 + return pending; 134 142 } 135 143 136 144 static inline void autofs4_copy_atime(struct file *src, struct file *dst)
+14 -2
fs/autofs4/expire.c
··· 99 99 if (!autofs4_can_expire(top, timeout, do_now)) 100 100 return 0; 101 101 102 + /* Is someone visiting anywhere in the tree ? */ 103 + if (may_umount_tree(mnt)) 104 + return 0; 105 + 102 106 spin_lock(&dcache_lock); 103 107 repeat: 104 108 next = this_parent->d_subdirs.next; ··· 274 270 275 271 /* Case 2: tree mount, expire iff entire tree is not busy */ 276 272 if (!exp_leaves) { 273 + /* Lock the tree as we must expire as a whole */ 274 + spin_lock(&sbi->fs_lock); 277 275 if (autofs4_check_tree(mnt, dentry, timeout, do_now)) { 278 - expired = dentry; 279 - break; 276 + struct autofs_info *inf = autofs4_dentry_ino(dentry); 277 + 278 + /* Set this flag early to catch sys_chdir and the like */ 279 + inf->flags |= AUTOFS_INF_EXPIRING; 280 + spin_unlock(&sbi->fs_lock); 281 + expired = dentry; 282 + break; 280 283 } 284 + spin_unlock(&sbi->fs_lock); 281 285 /* Case 3: direct mount, expire individual leaves */ 282 286 } else { 283 287 expired = autofs4_check_leaves(mnt, dentry, timeout, do_now);
+1
fs/autofs4/inode.c
··· 206 206 sbi->version = 0; 207 207 sbi->sub_version = 0; 208 208 init_MUTEX(&sbi->wq_sem); 209 + spin_lock_init(&sbi->fs_lock); 209 210 sbi->queues = NULL; 210 211 s->s_blocksize = 1024; 211 212 s->s_blocksize_bits = 10;