aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIan Kent <raven@themaw.net>2008-07-24 00:30:27 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-07-24 13:47:32 -0400
commit6e60a9ab5f5d314735467752f623072f5b75157a (patch)
tree1a42df40e6fad52bd6be74970af8eb56870fe6ba
parent97e7449a7ad883bf9f516fc970778d75999c7843 (diff)
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>
-rw-r--r--fs/autofs4/autofs_i.h3
-rw-r--r--fs/autofs4/expire.c16
-rw-r--r--fs/autofs4/root.c72
3 files changed, 65 insertions, 26 deletions
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h
index 5d90ed3b4b43..4b40cbc71e9b 100644
--- a/fs/autofs4/autofs_i.h
+++ b/fs/autofs4/autofs_i.h
@@ -52,6 +52,8 @@ struct autofs_info {
52 52
53 int flags; 53 int flags;
54 54
55 struct completion expire_complete;
56
55 struct list_head active; 57 struct list_head active;
56 struct list_head expiring; 58 struct list_head expiring;
57 59
@@ -69,6 +71,7 @@ struct autofs_info {
69}; 71};
70 72
71#define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ 73#define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */
74#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */
72 75
73struct autofs_wait_queue { 76struct autofs_wait_queue {
74 wait_queue_head_t queue; 77 wait_queue_head_t queue;
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,
259 now = jiffies; 259 now = jiffies;
260 timeout = sbi->exp_timeout; 260 timeout = sbi->exp_timeout;
261 261
262 /* Lock the tree as we must expire as a whole */
263 spin_lock(&sbi->fs_lock); 262 spin_lock(&sbi->fs_lock);
264 if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { 263 if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
265 struct autofs_info *ino = autofs4_dentry_ino(root); 264 struct autofs_info *ino = autofs4_dentry_ino(root);
266 265 if (d_mountpoint(root)) {
267 /* Set this flag early to catch sys_chdir and the like */ 266 ino->flags |= AUTOFS_INF_MOUNTPOINT;
267 root->d_mounted--;
268 }
268 ino->flags |= AUTOFS_INF_EXPIRING; 269 ino->flags |= AUTOFS_INF_EXPIRING;
270 init_completion(&ino->expire_complete);
269 spin_unlock(&sbi->fs_lock); 271 spin_unlock(&sbi->fs_lock);
270 return root; 272 return root;
271 } 273 }
@@ -392,6 +394,7 @@ found:
392 expired, (int)expired->d_name.len, expired->d_name.name); 394 expired, (int)expired->d_name.len, expired->d_name.name);
393 ino = autofs4_dentry_ino(expired); 395 ino = autofs4_dentry_ino(expired);
394 ino->flags |= AUTOFS_INF_EXPIRING; 396 ino->flags |= AUTOFS_INF_EXPIRING;
397 init_completion(&ino->expire_complete);
395 spin_unlock(&sbi->fs_lock); 398 spin_unlock(&sbi->fs_lock);
396 spin_lock(&dcache_lock); 399 spin_lock(&dcache_lock);
397 list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); 400 list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child);
@@ -429,6 +432,7 @@ int autofs4_expire_run(struct super_block *sb,
429 spin_lock(&sbi->fs_lock); 432 spin_lock(&sbi->fs_lock);
430 ino = autofs4_dentry_ino(dentry); 433 ino = autofs4_dentry_ino(dentry);
431 ino->flags &= ~AUTOFS_INF_EXPIRING; 434 ino->flags &= ~AUTOFS_INF_EXPIRING;
435 complete_all(&ino->expire_complete);
432 spin_unlock(&sbi->fs_lock); 436 spin_unlock(&sbi->fs_lock);
433 437
434 return ret; 438 return ret;
@@ -457,8 +461,14 @@ int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt,
457 /* This is synchronous because it makes the daemon a 461 /* This is synchronous because it makes the daemon a
458 little easier */ 462 little easier */
459 ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); 463 ret = autofs4_wait(sbi, dentry, NFY_EXPIRE);
464
460 spin_lock(&sbi->fs_lock); 465 spin_lock(&sbi->fs_lock);
466 if (ino->flags & AUTOFS_INF_MOUNTPOINT) {
467 sb->s_root->d_mounted++;
468 ino->flags &= ~AUTOFS_INF_MOUNTPOINT;
469 }
461 ino->flags &= ~AUTOFS_INF_EXPIRING; 470 ino->flags &= ~AUTOFS_INF_EXPIRING;
471 complete_all(&ino->expire_complete);
462 spin_unlock(&sbi->fs_lock); 472 spin_unlock(&sbi->fs_lock);
463 dput(dentry); 473 dput(dentry);
464 } 474 }
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index 1c2579de1f2e..adbd8559e870 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -141,6 +141,7 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags)
141 dentry, dentry->d_name.len, dentry->d_name.name); 141 dentry, dentry->d_name.len, dentry->d_name.name);
142 142
143 status = autofs4_wait(sbi, dentry, NFY_NONE); 143 status = autofs4_wait(sbi, dentry, NFY_NONE);
144 wait_for_completion(&ino->expire_complete);
144 145
145 DPRINTK("expire done status=%d", status); 146 DPRINTK("expire done status=%d", status);
146 147
@@ -227,14 +228,32 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
227 DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", 228 DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d",
228 dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, 229 dentry, dentry->d_name.len, dentry->d_name.name, oz_mode,
229 nd->flags); 230 nd->flags);
230 231 /*
231 /* If it's our master or we shouldn't trigger a mount we're done */ 232 * For an expire of a covered direct or offset mount we need
232 lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); 233 * to beeak out of follow_down() at the autofs mount trigger
233 if (oz_mode || 234 * (d_mounted--), so we can see the expiring flag, and manage
234 !(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) 235 * the blocking and following here until the expire is completed.
236 */
237 if (oz_mode) {
238 spin_lock(&sbi->fs_lock);
239 if (ino->flags & AUTOFS_INF_EXPIRING) {
240 spin_unlock(&sbi->fs_lock);
241 /* Follow down to our covering mount. */
242 if (!follow_down(&nd->path.mnt, &nd->path.dentry))
243 goto done;
244 /*
245 * We shouldn't need to do this but we have no way
246 * of knowing what may have been done so try a follow
247 * just in case.
248 */
249 autofs4_follow_mount(&nd->path.mnt, &nd->path.dentry);
250 goto done;
251 }
252 spin_unlock(&sbi->fs_lock);
235 goto done; 253 goto done;
254 }
236 255
237 /* If an expire request is pending wait for it. */ 256 /* If an expire request is pending everyone must wait. */
238 spin_lock(&sbi->fs_lock); 257 spin_lock(&sbi->fs_lock);
239 if (ino->flags & AUTOFS_INF_EXPIRING) { 258 if (ino->flags & AUTOFS_INF_EXPIRING) {
240 spin_unlock(&sbi->fs_lock); 259 spin_unlock(&sbi->fs_lock);
@@ -243,6 +262,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
243 dentry, dentry->d_name.len, dentry->d_name.name); 262 dentry, dentry->d_name.len, dentry->d_name.name);
244 263
245 status = autofs4_wait(sbi, dentry, NFY_NONE); 264 status = autofs4_wait(sbi, dentry, NFY_NONE);
265 wait_for_completion(&ino->expire_complete);
246 266
247 DPRINTK("request done status=%d", status); 267 DPRINTK("request done status=%d", status);
248 268
@@ -250,10 +270,15 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
250 } 270 }
251 spin_unlock(&sbi->fs_lock); 271 spin_unlock(&sbi->fs_lock);
252cont: 272cont:
273 /* We trigger a mount for almost all flags */
274 lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS);
275 if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING))
276 goto done;
277
253 /* 278 /*
254 * If the dentry contains directories then it is an 279 * If the dentry contains directories then it is an autofs
255 * autofs multi-mount with no root mount offset. So 280 * multi-mount with no root mount offset. So don't try to
256 * don't try to mount it again. 281 * mount it again.
257 */ 282 */
258 spin_lock(&dcache_lock); 283 spin_lock(&dcache_lock);
259 if (dentry->d_flags & DCACHE_AUTOFS_PENDING || 284 if (dentry->d_flags & DCACHE_AUTOFS_PENDING ||
@@ -264,22 +289,22 @@ cont:
264 if (status) 289 if (status)
265 goto out_error; 290 goto out_error;
266 291
267 /* 292 goto follow;
268 * The mount succeeded but if there is no root mount
269 * it must be an autofs multi-mount with no root offset
270 * so we don't need to follow the mount.
271 */
272 if (d_mountpoint(dentry)) {
273 if (!autofs4_follow_mount(&nd->path.mnt,
274 &nd->path.dentry)) {
275 status = -ENOENT;
276 goto out_error;
277 }
278 }
279
280 goto done;
281 } 293 }
282 spin_unlock(&dcache_lock); 294 spin_unlock(&dcache_lock);
295follow:
296 /*
297 * If there is no root mount it must be an autofs
298 * multi-mount with no root offset so we don't need
299 * to follow it.
300 */
301 if (d_mountpoint(dentry)) {
302 if (!autofs4_follow_mount(&nd->path.mnt,
303 &nd->path.dentry)) {
304 status = -ENOENT;
305 goto out_error;
306 }
307 }
283 308
284done: 309done:
285 return NULL; 310 return NULL;
@@ -545,6 +570,7 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
545 expiring, expiring->d_name.len, 570 expiring, expiring->d_name.len,
546 expiring->d_name.name); 571 expiring->d_name.name);
547 autofs4_wait(sbi, expiring, NFY_NONE); 572 autofs4_wait(sbi, expiring, NFY_NONE);
573 wait_for_completion(&ino->expire_complete);
548 DPRINTK("request completed"); 574 DPRINTK("request completed");
549 goto cont; 575 goto cont;
550 } 576 }