aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIan Kent <raven@themaw.net>2008-07-24 00:30:26 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-07-24 13:47:32 -0400
commit97e7449a7ad883bf9f516fc970778d75999c7843 (patch)
tree903f6de243847eb12d1a372b271b15046ecdc774
parent26e81b3142f1ba497d4cd0365c13661684b784ce (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>
-rw-r--r--fs/autofs4/autofs_i.h10
-rw-r--r--fs/autofs4/expire.c46
-rw-r--r--fs/autofs4/root.c32
3 files changed, 65 insertions, 23 deletions
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h
index 058e1800caec..5d90ed3b4b43 100644
--- a/fs/autofs4/autofs_i.h
+++ b/fs/autofs4/autofs_i.h
@@ -138,18 +138,14 @@ static inline int autofs4_oz_mode(struct autofs_sb_info *sbi) {
138static inline int autofs4_ispending(struct dentry *dentry) 138static inline int autofs4_ispending(struct dentry *dentry)
139{ 139{
140 struct autofs_info *inf = autofs4_dentry_ino(dentry); 140 struct autofs_info *inf = autofs4_dentry_ino(dentry);
141 int pending = 0;
142 141
143 if (dentry->d_flags & DCACHE_AUTOFS_PENDING) 142 if (dentry->d_flags & DCACHE_AUTOFS_PENDING)
144 return 1; 143 return 1;
145 144
146 if (inf) { 145 if (inf->flags & AUTOFS_INF_EXPIRING)
147 spin_lock(&inf->sbi->fs_lock); 146 return 1;
148 pending = inf->flags & AUTOFS_INF_EXPIRING;
149 spin_unlock(&inf->sbi->fs_lock);
150 }
151 147
152 return pending; 148 return 0;
153} 149}
154 150
155static inline void autofs4_copy_atime(struct file *src, struct file *dst) 151static inline void autofs4_copy_atime(struct file *src, struct file *dst)
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 }
369next: 381next:
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:
377found: 390found:
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
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index 61d1dca16884..1c2579de1f2e 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -133,7 +133,10 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags)
133 /* Block on any pending expiry here; invalidate the dentry 133 /* Block on any pending expiry here; invalidate the dentry
134 when expiration is done to trigger mount request with a new 134 when expiration is done to trigger mount request with a new
135 dentry */ 135 dentry */
136 if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { 136 spin_lock(&sbi->fs_lock);
137 if (ino->flags & AUTOFS_INF_EXPIRING) {
138 spin_unlock(&sbi->fs_lock);
139
137 DPRINTK("waiting for expire %p name=%.*s", 140 DPRINTK("waiting for expire %p name=%.*s",
138 dentry, dentry->d_name.len, dentry->d_name.name); 141 dentry, dentry->d_name.len, dentry->d_name.name);
139 142
@@ -149,8 +152,11 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags)
149 status = d_invalidate(dentry); 152 status = d_invalidate(dentry);
150 if (status != -EBUSY) 153 if (status != -EBUSY)
151 return -EAGAIN; 154 return -EAGAIN;
152 }
153 155
156 goto cont;
157 }
158 spin_unlock(&sbi->fs_lock);
159cont:
154 DPRINTK("dentry=%p %.*s ino=%p", 160 DPRINTK("dentry=%p %.*s ino=%p",
155 dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); 161 dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
156 162
@@ -229,15 +235,21 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
229 goto done; 235 goto done;
230 236
231 /* If an expire request is pending wait for it. */ 237 /* If an expire request is pending wait for it. */
232 if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { 238 spin_lock(&sbi->fs_lock);
239 if (ino->flags & AUTOFS_INF_EXPIRING) {
240 spin_unlock(&sbi->fs_lock);
241
233 DPRINTK("waiting for active request %p name=%.*s", 242 DPRINTK("waiting for active request %p name=%.*s",
234 dentry, dentry->d_name.len, dentry->d_name.name); 243 dentry, dentry->d_name.len, dentry->d_name.name);
235 244
236 status = autofs4_wait(sbi, dentry, NFY_NONE); 245 status = autofs4_wait(sbi, dentry, NFY_NONE);
237 246
238 DPRINTK("request done status=%d", status); 247 DPRINTK("request done status=%d", status);
239 }
240 248
249 goto cont;
250 }
251 spin_unlock(&sbi->fs_lock);
252cont:
241 /* 253 /*
242 * If the dentry contains directories then it is an 254 * If the dentry contains directories then it is an
243 * autofs multi-mount with no root mount offset. So 255 * autofs multi-mount with no root mount offset. So
@@ -292,8 +304,11 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
292 int status = 1; 304 int status = 1;
293 305
294 /* Pending dentry */ 306 /* Pending dentry */
307 spin_lock(&sbi->fs_lock);
295 if (autofs4_ispending(dentry)) { 308 if (autofs4_ispending(dentry)) {
296 /* The daemon never causes a mount to trigger */ 309 /* The daemon never causes a mount to trigger */
310 spin_unlock(&sbi->fs_lock);
311
297 if (oz_mode) 312 if (oz_mode)
298 return 1; 313 return 1;
299 314
@@ -316,6 +331,7 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
316 331
317 return status; 332 return status;
318 } 333 }
334 spin_unlock(&sbi->fs_lock);
319 335
320 /* Negative dentry.. invalidate if "old" */ 336 /* Negative dentry.. invalidate if "old" */
321 if (dentry->d_inode == NULL) 337 if (dentry->d_inode == NULL)
@@ -329,6 +345,7 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
329 DPRINTK("dentry=%p %.*s, emptydir", 345 DPRINTK("dentry=%p %.*s, emptydir",
330 dentry, dentry->d_name.len, dentry->d_name.name); 346 dentry, dentry->d_name.len, dentry->d_name.name);
331 spin_unlock(&dcache_lock); 347 spin_unlock(&dcache_lock);
348
332 /* The daemon never causes a mount to trigger */ 349 /* The daemon never causes a mount to trigger */
333 if (oz_mode) 350 if (oz_mode)
334 return 1; 351 return 1;
@@ -521,13 +538,18 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
521 * so it must have been successful, so just wait for it. 538 * so it must have been successful, so just wait for it.
522 */ 539 */
523 ino = autofs4_dentry_ino(expiring); 540 ino = autofs4_dentry_ino(expiring);
524 while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { 541 spin_lock(&sbi->fs_lock);
542 if (ino->flags & AUTOFS_INF_EXPIRING) {
543 spin_unlock(&sbi->fs_lock);
525 DPRINTK("wait for incomplete expire %p name=%.*s", 544 DPRINTK("wait for incomplete expire %p name=%.*s",
526 expiring, expiring->d_name.len, 545 expiring, expiring->d_name.len,
527 expiring->d_name.name); 546 expiring->d_name.name);
528 autofs4_wait(sbi, expiring, NFY_NONE); 547 autofs4_wait(sbi, expiring, NFY_NONE);
529 DPRINTK("request completed"); 548 DPRINTK("request completed");
549 goto cont;
530 } 550 }
551 spin_unlock(&sbi->fs_lock);
552cont:
531 spin_lock(&sbi->lookup_lock); 553 spin_lock(&sbi->lookup_lock);
532 if (!list_empty(&ino->expiring)) 554 if (!list_empty(&ino->expiring))
533 list_del_init(&ino->expiring); 555 list_del_init(&ino->expiring);