Commit 3a9720ce authored by Ian Kent's avatar Ian Kent Committed by Linus Torvalds
Browse files

[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: default avatarIan Kent <raven@themaw.net>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 4dcd00b1
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -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)
+14 −2
Original line number Diff line number Diff line
@@ -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)) {
				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);
+1 −0
Original line number Diff line number Diff line
@@ -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;