diff options
author | Ian Kent <raven@themaw.net> | 2008-07-24 00:30:27 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-24 13:47:32 -0400 |
commit | 6e60a9ab5f5d314735467752f623072f5b75157a (patch) | |
tree | 1a42df40e6fad52bd6be74970af8eb56870fe6ba /fs/autofs4/root.c | |
parent | 97e7449a7ad883bf9f516fc970778d75999c7843 (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>
Diffstat (limited to 'fs/autofs4/root.c')
-rw-r--r-- | fs/autofs4/root.c | 72 |
1 files changed, 49 insertions, 23 deletions
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); |
252 | cont: | 272 | cont: |
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); |
295 | follow: | ||
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 | ||
284 | done: | 309 | done: |
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 | } |