diff options
-rw-r--r-- | Documentation/filesystems/Locking | 2 | ||||
-rw-r--r-- | Documentation/filesystems/vfs.txt | 2 | ||||
-rw-r--r-- | fs/autofs4/autofs_i.h | 5 | ||||
-rw-r--r-- | fs/autofs4/dev-ioctl.c | 4 | ||||
-rw-r--r-- | fs/autofs4/expire.c | 25 | ||||
-rw-r--r-- | fs/autofs4/root.c | 61 | ||||
-rw-r--r-- | fs/autofs4/waitq.c | 13 | ||||
-rw-r--r-- | fs/dcache.c | 40 | ||||
-rw-r--r-- | fs/mount.h | 6 | ||||
-rw-r--r-- | fs/namei.c | 13 | ||||
-rw-r--r-- | fs/namespace.c | 29 | ||||
-rw-r--r-- | include/linux/dcache.h | 4 | ||||
-rw-r--r-- | include/linux/mount.h | 2 |
13 files changed, 131 insertions, 75 deletions
diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 1b5f15653b1b..4ca3a8d1349d 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking | |||
@@ -20,7 +20,7 @@ prototypes: | |||
20 | void (*d_iput)(struct dentry *, struct inode *); | 20 | void (*d_iput)(struct dentry *, struct inode *); |
21 | char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen); | 21 | char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen); |
22 | struct vfsmount *(*d_automount)(struct path *path); | 22 | struct vfsmount *(*d_automount)(struct path *path); |
23 | int (*d_manage)(struct dentry *, bool); | 23 | int (*d_manage)(const struct path *, bool); |
24 | struct dentry *(*d_real)(struct dentry *, const struct inode *, | 24 | struct dentry *(*d_real)(struct dentry *, const struct inode *, |
25 | unsigned int); | 25 | unsigned int); |
26 | 26 | ||
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index b5039a00caaf..3893f4d44cd4 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt | |||
@@ -948,7 +948,7 @@ struct dentry_operations { | |||
948 | void (*d_iput)(struct dentry *, struct inode *); | 948 | void (*d_iput)(struct dentry *, struct inode *); |
949 | char *(*d_dname)(struct dentry *, char *, int); | 949 | char *(*d_dname)(struct dentry *, char *, int); |
950 | struct vfsmount *(*d_automount)(struct path *); | 950 | struct vfsmount *(*d_automount)(struct path *); |
951 | int (*d_manage)(struct dentry *, bool); | 951 | int (*d_manage)(const struct path *, bool); |
952 | struct dentry *(*d_real)(struct dentry *, const struct inode *, | 952 | struct dentry *(*d_real)(struct dentry *, const struct inode *, |
953 | unsigned int); | 953 | unsigned int); |
954 | }; | 954 | }; |
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index a1fba4285277..c885daae68c8 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h | |||
@@ -145,7 +145,7 @@ void autofs4_free_ino(struct autofs_info *); | |||
145 | 145 | ||
146 | /* Expiration */ | 146 | /* Expiration */ |
147 | int is_autofs4_dentry(struct dentry *); | 147 | int is_autofs4_dentry(struct dentry *); |
148 | int autofs4_expire_wait(struct dentry *dentry, int rcu_walk); | 148 | int autofs4_expire_wait(const struct path *path, int rcu_walk); |
149 | int autofs4_expire_run(struct super_block *, struct vfsmount *, | 149 | int autofs4_expire_run(struct super_block *, struct vfsmount *, |
150 | struct autofs_sb_info *, | 150 | struct autofs_sb_info *, |
151 | struct autofs_packet_expire __user *); | 151 | struct autofs_packet_expire __user *); |
@@ -217,7 +217,8 @@ static inline int autofs_prepare_pipe(struct file *pipe) | |||
217 | 217 | ||
218 | /* Queue management functions */ | 218 | /* Queue management functions */ |
219 | 219 | ||
220 | int autofs4_wait(struct autofs_sb_info *, struct dentry *, enum autofs_notify); | 220 | int autofs4_wait(struct autofs_sb_info *, |
221 | const struct path *, enum autofs_notify); | ||
221 | int autofs4_wait_release(struct autofs_sb_info *, autofs_wqt_t, int); | 222 | int autofs4_wait_release(struct autofs_sb_info *, autofs_wqt_t, int); |
222 | void autofs4_catatonic_mode(struct autofs_sb_info *); | 223 | void autofs4_catatonic_mode(struct autofs_sb_info *); |
223 | 224 | ||
diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c index dfc6f49ee597..6f48d670c941 100644 --- a/fs/autofs4/dev-ioctl.c +++ b/fs/autofs4/dev-ioctl.c | |||
@@ -468,7 +468,7 @@ static int autofs_dev_ioctl_requester(struct file *fp, | |||
468 | ino = autofs4_dentry_ino(path.dentry); | 468 | ino = autofs4_dentry_ino(path.dentry); |
469 | if (ino) { | 469 | if (ino) { |
470 | err = 0; | 470 | err = 0; |
471 | autofs4_expire_wait(path.dentry, 0); | 471 | autofs4_expire_wait(&path, 0); |
472 | spin_lock(&sbi->fs_lock); | 472 | spin_lock(&sbi->fs_lock); |
473 | param->requester.uid = | 473 | param->requester.uid = |
474 | from_kuid_munged(current_user_ns(), ino->uid); | 474 | from_kuid_munged(current_user_ns(), ino->uid); |
@@ -575,7 +575,7 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp, | |||
575 | 575 | ||
576 | devid = new_encode_dev(dev); | 576 | devid = new_encode_dev(dev); |
577 | 577 | ||
578 | err = have_submounts(path.dentry); | 578 | err = path_has_submounts(&path); |
579 | 579 | ||
580 | if (follow_down_one(&path)) | 580 | if (follow_down_one(&path)) |
581 | magic = path.dentry->d_sb->s_magic; | 581 | magic = path.dentry->d_sb->s_magic; |
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index d8e6d421c27f..57725d4a8c59 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c | |||
@@ -310,26 +310,29 @@ struct dentry *autofs4_expire_direct(struct super_block *sb, | |||
310 | now = jiffies; | 310 | now = jiffies; |
311 | timeout = sbi->exp_timeout; | 311 | timeout = sbi->exp_timeout; |
312 | 312 | ||
313 | spin_lock(&sbi->fs_lock); | ||
314 | ino = autofs4_dentry_ino(root); | ||
315 | /* No point expiring a pending mount */ | ||
316 | if (ino->flags & AUTOFS_INF_PENDING) | ||
317 | goto out; | ||
318 | if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { | 313 | if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { |
314 | spin_lock(&sbi->fs_lock); | ||
315 | ino = autofs4_dentry_ino(root); | ||
316 | /* No point expiring a pending mount */ | ||
317 | if (ino->flags & AUTOFS_INF_PENDING) { | ||
318 | spin_unlock(&sbi->fs_lock); | ||
319 | goto out; | ||
320 | } | ||
319 | ino->flags |= AUTOFS_INF_WANT_EXPIRE; | 321 | ino->flags |= AUTOFS_INF_WANT_EXPIRE; |
320 | spin_unlock(&sbi->fs_lock); | 322 | spin_unlock(&sbi->fs_lock); |
321 | synchronize_rcu(); | 323 | synchronize_rcu(); |
322 | spin_lock(&sbi->fs_lock); | ||
323 | if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { | 324 | if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { |
325 | spin_lock(&sbi->fs_lock); | ||
324 | ino->flags |= AUTOFS_INF_EXPIRING; | 326 | ino->flags |= AUTOFS_INF_EXPIRING; |
325 | init_completion(&ino->expire_complete); | 327 | init_completion(&ino->expire_complete); |
326 | spin_unlock(&sbi->fs_lock); | 328 | spin_unlock(&sbi->fs_lock); |
327 | return root; | 329 | return root; |
328 | } | 330 | } |
331 | spin_lock(&sbi->fs_lock); | ||
329 | ino->flags &= ~AUTOFS_INF_WANT_EXPIRE; | 332 | ino->flags &= ~AUTOFS_INF_WANT_EXPIRE; |
333 | spin_unlock(&sbi->fs_lock); | ||
330 | } | 334 | } |
331 | out: | 335 | out: |
332 | spin_unlock(&sbi->fs_lock); | ||
333 | dput(root); | 336 | dput(root); |
334 | 337 | ||
335 | return NULL; | 338 | return NULL; |
@@ -495,8 +498,9 @@ found: | |||
495 | return expired; | 498 | return expired; |
496 | } | 499 | } |
497 | 500 | ||
498 | int autofs4_expire_wait(struct dentry *dentry, int rcu_walk) | 501 | int autofs4_expire_wait(const struct path *path, int rcu_walk) |
499 | { | 502 | { |
503 | struct dentry *dentry = path->dentry; | ||
500 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); | 504 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); |
501 | struct autofs_info *ino = autofs4_dentry_ino(dentry); | 505 | struct autofs_info *ino = autofs4_dentry_ino(dentry); |
502 | int status; | 506 | int status; |
@@ -525,7 +529,7 @@ retry: | |||
525 | 529 | ||
526 | pr_debug("waiting for expire %p name=%pd\n", dentry, dentry); | 530 | pr_debug("waiting for expire %p name=%pd\n", dentry, dentry); |
527 | 531 | ||
528 | status = autofs4_wait(sbi, dentry, NFY_NONE); | 532 | status = autofs4_wait(sbi, path, NFY_NONE); |
529 | wait_for_completion(&ino->expire_complete); | 533 | wait_for_completion(&ino->expire_complete); |
530 | 534 | ||
531 | pr_debug("expire done status=%d\n", status); | 535 | pr_debug("expire done status=%d\n", status); |
@@ -592,11 +596,12 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, | |||
592 | 596 | ||
593 | if (dentry) { | 597 | if (dentry) { |
594 | struct autofs_info *ino = autofs4_dentry_ino(dentry); | 598 | struct autofs_info *ino = autofs4_dentry_ino(dentry); |
599 | const struct path path = { .mnt = mnt, .dentry = dentry }; | ||
595 | 600 | ||
596 | /* This is synchronous because it makes the daemon a | 601 | /* This is synchronous because it makes the daemon a |
597 | * little easier | 602 | * little easier |
598 | */ | 603 | */ |
599 | ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); | 604 | ret = autofs4_wait(sbi, &path, NFY_EXPIRE); |
600 | 605 | ||
601 | spin_lock(&sbi->fs_lock); | 606 | spin_lock(&sbi->fs_lock); |
602 | /* avoid rapid-fire expire attempts if expiry fails */ | 607 | /* avoid rapid-fire expire attempts if expiry fails */ |
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index a11f73174877..82e8f6edfb48 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c | |||
@@ -32,7 +32,7 @@ static int autofs4_dir_open(struct inode *inode, struct file *file); | |||
32 | static struct dentry *autofs4_lookup(struct inode *, | 32 | static struct dentry *autofs4_lookup(struct inode *, |
33 | struct dentry *, unsigned int); | 33 | struct dentry *, unsigned int); |
34 | static struct vfsmount *autofs4_d_automount(struct path *); | 34 | static struct vfsmount *autofs4_d_automount(struct path *); |
35 | static int autofs4_d_manage(struct dentry *, bool); | 35 | static int autofs4_d_manage(const struct path *, bool); |
36 | static void autofs4_dentry_release(struct dentry *); | 36 | static void autofs4_dentry_release(struct dentry *); |
37 | 37 | ||
38 | const struct file_operations autofs4_root_operations = { | 38 | const struct file_operations autofs4_root_operations = { |
@@ -123,7 +123,7 @@ static int autofs4_dir_open(struct inode *inode, struct file *file) | |||
123 | * it. | 123 | * it. |
124 | */ | 124 | */ |
125 | spin_lock(&sbi->lookup_lock); | 125 | spin_lock(&sbi->lookup_lock); |
126 | if (!d_mountpoint(dentry) && simple_empty(dentry)) { | 126 | if (!path_is_mountpoint(&file->f_path) && simple_empty(dentry)) { |
127 | spin_unlock(&sbi->lookup_lock); | 127 | spin_unlock(&sbi->lookup_lock); |
128 | return -ENOENT; | 128 | return -ENOENT; |
129 | } | 129 | } |
@@ -269,39 +269,41 @@ next: | |||
269 | return NULL; | 269 | return NULL; |
270 | } | 270 | } |
271 | 271 | ||
272 | static int autofs4_mount_wait(struct dentry *dentry, bool rcu_walk) | 272 | static int autofs4_mount_wait(const struct path *path, bool rcu_walk) |
273 | { | 273 | { |
274 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); | 274 | struct autofs_sb_info *sbi = autofs4_sbi(path->dentry->d_sb); |
275 | struct autofs_info *ino = autofs4_dentry_ino(dentry); | 275 | struct autofs_info *ino = autofs4_dentry_ino(path->dentry); |
276 | int status = 0; | 276 | int status = 0; |
277 | 277 | ||
278 | if (ino->flags & AUTOFS_INF_PENDING) { | 278 | if (ino->flags & AUTOFS_INF_PENDING) { |
279 | if (rcu_walk) | 279 | if (rcu_walk) |
280 | return -ECHILD; | 280 | return -ECHILD; |
281 | pr_debug("waiting for mount name=%pd\n", dentry); | 281 | pr_debug("waiting for mount name=%pd\n", path->dentry); |
282 | status = autofs4_wait(sbi, dentry, NFY_MOUNT); | 282 | status = autofs4_wait(sbi, path, NFY_MOUNT); |
283 | pr_debug("mount wait done status=%d\n", status); | 283 | pr_debug("mount wait done status=%d\n", status); |
284 | } | 284 | } |
285 | ino->last_used = jiffies; | 285 | ino->last_used = jiffies; |
286 | return status; | 286 | return status; |
287 | } | 287 | } |
288 | 288 | ||
289 | static int do_expire_wait(struct dentry *dentry, bool rcu_walk) | 289 | static int do_expire_wait(const struct path *path, bool rcu_walk) |
290 | { | 290 | { |
291 | struct dentry *dentry = path->dentry; | ||
291 | struct dentry *expiring; | 292 | struct dentry *expiring; |
292 | 293 | ||
293 | expiring = autofs4_lookup_expiring(dentry, rcu_walk); | 294 | expiring = autofs4_lookup_expiring(dentry, rcu_walk); |
294 | if (IS_ERR(expiring)) | 295 | if (IS_ERR(expiring)) |
295 | return PTR_ERR(expiring); | 296 | return PTR_ERR(expiring); |
296 | if (!expiring) | 297 | if (!expiring) |
297 | return autofs4_expire_wait(dentry, rcu_walk); | 298 | return autofs4_expire_wait(path, rcu_walk); |
298 | else { | 299 | else { |
300 | const struct path this = { .mnt = path->mnt, .dentry = expiring }; | ||
299 | /* | 301 | /* |
300 | * If we are racing with expire the request might not | 302 | * If we are racing with expire the request might not |
301 | * be quite complete, but the directory has been removed | 303 | * be quite complete, but the directory has been removed |
302 | * so it must have been successful, just wait for it. | 304 | * so it must have been successful, just wait for it. |
303 | */ | 305 | */ |
304 | autofs4_expire_wait(expiring, 0); | 306 | autofs4_expire_wait(&this, 0); |
305 | autofs4_del_expiring(expiring); | 307 | autofs4_del_expiring(expiring); |
306 | dput(expiring); | 308 | dput(expiring); |
307 | } | 309 | } |
@@ -354,7 +356,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path) | |||
354 | * and the directory was removed, so just go ahead and try | 356 | * and the directory was removed, so just go ahead and try |
355 | * the mount. | 357 | * the mount. |
356 | */ | 358 | */ |
357 | status = do_expire_wait(dentry, 0); | 359 | status = do_expire_wait(path, 0); |
358 | if (status && status != -EAGAIN) | 360 | if (status && status != -EAGAIN) |
359 | return NULL; | 361 | return NULL; |
360 | 362 | ||
@@ -362,7 +364,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path) | |||
362 | spin_lock(&sbi->fs_lock); | 364 | spin_lock(&sbi->fs_lock); |
363 | if (ino->flags & AUTOFS_INF_PENDING) { | 365 | if (ino->flags & AUTOFS_INF_PENDING) { |
364 | spin_unlock(&sbi->fs_lock); | 366 | spin_unlock(&sbi->fs_lock); |
365 | status = autofs4_mount_wait(dentry, 0); | 367 | status = autofs4_mount_wait(path, 0); |
366 | if (status) | 368 | if (status) |
367 | return ERR_PTR(status); | 369 | return ERR_PTR(status); |
368 | goto done; | 370 | goto done; |
@@ -370,28 +372,28 @@ static struct vfsmount *autofs4_d_automount(struct path *path) | |||
370 | 372 | ||
371 | /* | 373 | /* |
372 | * If the dentry is a symlink it's equivalent to a directory | 374 | * If the dentry is a symlink it's equivalent to a directory |
373 | * having d_mountpoint() true, so there's no need to call back | 375 | * having path_is_mountpoint() true, so there's no need to call |
374 | * to the daemon. | 376 | * back to the daemon. |
375 | */ | 377 | */ |
376 | if (d_really_is_positive(dentry) && d_is_symlink(dentry)) { | 378 | if (d_really_is_positive(dentry) && d_is_symlink(dentry)) { |
377 | spin_unlock(&sbi->fs_lock); | 379 | spin_unlock(&sbi->fs_lock); |
378 | goto done; | 380 | goto done; |
379 | } | 381 | } |
380 | 382 | ||
381 | if (!d_mountpoint(dentry)) { | 383 | if (!path_is_mountpoint(path)) { |
382 | /* | 384 | /* |
383 | * It's possible that user space hasn't removed directories | 385 | * It's possible that user space hasn't removed directories |
384 | * after umounting a rootless multi-mount, although it | 386 | * after umounting a rootless multi-mount, although it |
385 | * should. For v5 have_submounts() is sufficient to handle | 387 | * should. For v5 path_has_submounts() is sufficient to |
386 | * this because the leaves of the directory tree under the | 388 | * handle this because the leaves of the directory tree under |
387 | * mount never trigger mounts themselves (they have an autofs | 389 | * the mount never trigger mounts themselves (they have an |
388 | * trigger mount mounted on them). But v4 pseudo direct mounts | 390 | * autofs trigger mount mounted on them). But v4 pseudo direct |
389 | * do need the leaves to trigger mounts. In this case we | 391 | * mounts do need the leaves to trigger mounts. In this case |
390 | * have no choice but to use the list_empty() check and | 392 | * we have no choice but to use the list_empty() check and |
391 | * require user space behave. | 393 | * require user space behave. |
392 | */ | 394 | */ |
393 | if (sbi->version > 4) { | 395 | if (sbi->version > 4) { |
394 | if (have_submounts(dentry)) { | 396 | if (path_has_submounts(path)) { |
395 | spin_unlock(&sbi->fs_lock); | 397 | spin_unlock(&sbi->fs_lock); |
396 | goto done; | 398 | goto done; |
397 | } | 399 | } |
@@ -403,7 +405,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path) | |||
403 | } | 405 | } |
404 | ino->flags |= AUTOFS_INF_PENDING; | 406 | ino->flags |= AUTOFS_INF_PENDING; |
405 | spin_unlock(&sbi->fs_lock); | 407 | spin_unlock(&sbi->fs_lock); |
406 | status = autofs4_mount_wait(dentry, 0); | 408 | status = autofs4_mount_wait(path, 0); |
407 | spin_lock(&sbi->fs_lock); | 409 | spin_lock(&sbi->fs_lock); |
408 | ino->flags &= ~AUTOFS_INF_PENDING; | 410 | ino->flags &= ~AUTOFS_INF_PENDING; |
409 | if (status) { | 411 | if (status) { |
@@ -421,8 +423,9 @@ done: | |||
421 | return NULL; | 423 | return NULL; |
422 | } | 424 | } |
423 | 425 | ||
424 | static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk) | 426 | static int autofs4_d_manage(const struct path *path, bool rcu_walk) |
425 | { | 427 | { |
428 | struct dentry *dentry = path->dentry; | ||
426 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); | 429 | struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); |
427 | struct autofs_info *ino = autofs4_dentry_ino(dentry); | 430 | struct autofs_info *ino = autofs4_dentry_ino(dentry); |
428 | int status; | 431 | int status; |
@@ -431,20 +434,20 @@ static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk) | |||
431 | 434 | ||
432 | /* The daemon never waits. */ | 435 | /* The daemon never waits. */ |
433 | if (autofs4_oz_mode(sbi)) { | 436 | if (autofs4_oz_mode(sbi)) { |
434 | if (!d_mountpoint(dentry)) | 437 | if (!path_is_mountpoint(path)) |
435 | return -EISDIR; | 438 | return -EISDIR; |
436 | return 0; | 439 | return 0; |
437 | } | 440 | } |
438 | 441 | ||
439 | /* Wait for pending expires */ | 442 | /* Wait for pending expires */ |
440 | if (do_expire_wait(dentry, rcu_walk) == -ECHILD) | 443 | if (do_expire_wait(path, rcu_walk) == -ECHILD) |
441 | return -ECHILD; | 444 | return -ECHILD; |
442 | 445 | ||
443 | /* | 446 | /* |
444 | * This dentry may be under construction so wait on mount | 447 | * This dentry may be under construction so wait on mount |
445 | * completion. | 448 | * completion. |
446 | */ | 449 | */ |
447 | status = autofs4_mount_wait(dentry, rcu_walk); | 450 | status = autofs4_mount_wait(path, rcu_walk); |
448 | if (status) | 451 | if (status) |
449 | return status; | 452 | return status; |
450 | 453 | ||
@@ -460,7 +463,7 @@ static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk) | |||
460 | 463 | ||
461 | if (ino->flags & AUTOFS_INF_WANT_EXPIRE) | 464 | if (ino->flags & AUTOFS_INF_WANT_EXPIRE) |
462 | return 0; | 465 | return 0; |
463 | if (d_mountpoint(dentry)) | 466 | if (path_is_mountpoint(path)) |
464 | return 0; | 467 | return 0; |
465 | inode = d_inode_rcu(dentry); | 468 | inode = d_inode_rcu(dentry); |
466 | if (inode && S_ISLNK(inode->i_mode)) | 469 | if (inode && S_ISLNK(inode->i_mode)) |
@@ -487,7 +490,7 @@ static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk) | |||
487 | * we can avoid needless calls ->d_automount() and avoid | 490 | * we can avoid needless calls ->d_automount() and avoid |
488 | * an incorrect ELOOP error return. | 491 | * an incorrect ELOOP error return. |
489 | */ | 492 | */ |
490 | if ((!d_mountpoint(dentry) && !simple_empty(dentry)) || | 493 | if ((!path_is_mountpoint(path) && !simple_empty(dentry)) || |
491 | (d_really_is_positive(dentry) && d_is_symlink(dentry))) | 494 | (d_really_is_positive(dentry) && d_is_symlink(dentry))) |
492 | status = -EISDIR; | 495 | status = -EISDIR; |
493 | } | 496 | } |
diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index e44271dfceb6..1278335ce366 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c | |||
@@ -250,8 +250,9 @@ autofs4_find_wait(struct autofs_sb_info *sbi, const struct qstr *qstr) | |||
250 | static int validate_request(struct autofs_wait_queue **wait, | 250 | static int validate_request(struct autofs_wait_queue **wait, |
251 | struct autofs_sb_info *sbi, | 251 | struct autofs_sb_info *sbi, |
252 | const struct qstr *qstr, | 252 | const struct qstr *qstr, |
253 | struct dentry *dentry, enum autofs_notify notify) | 253 | const struct path *path, enum autofs_notify notify) |
254 | { | 254 | { |
255 | struct dentry *dentry = path->dentry; | ||
255 | struct autofs_wait_queue *wq; | 256 | struct autofs_wait_queue *wq; |
256 | struct autofs_info *ino; | 257 | struct autofs_info *ino; |
257 | 258 | ||
@@ -314,6 +315,7 @@ static int validate_request(struct autofs_wait_queue **wait, | |||
314 | */ | 315 | */ |
315 | if (notify == NFY_MOUNT) { | 316 | if (notify == NFY_MOUNT) { |
316 | struct dentry *new = NULL; | 317 | struct dentry *new = NULL; |
318 | struct path this; | ||
317 | int valid = 1; | 319 | int valid = 1; |
318 | 320 | ||
319 | /* | 321 | /* |
@@ -333,7 +335,9 @@ static int validate_request(struct autofs_wait_queue **wait, | |||
333 | dentry = new; | 335 | dentry = new; |
334 | } | 336 | } |
335 | } | 337 | } |
336 | if (have_submounts(dentry)) | 338 | this.mnt = path->mnt; |
339 | this.dentry = dentry; | ||
340 | if (path_has_submounts(&this)) | ||
337 | valid = 0; | 341 | valid = 0; |
338 | 342 | ||
339 | if (new) | 343 | if (new) |
@@ -345,8 +349,9 @@ static int validate_request(struct autofs_wait_queue **wait, | |||
345 | } | 349 | } |
346 | 350 | ||
347 | int autofs4_wait(struct autofs_sb_info *sbi, | 351 | int autofs4_wait(struct autofs_sb_info *sbi, |
348 | struct dentry *dentry, enum autofs_notify notify) | 352 | const struct path *path, enum autofs_notify notify) |
349 | { | 353 | { |
354 | struct dentry *dentry = path->dentry; | ||
350 | struct autofs_wait_queue *wq; | 355 | struct autofs_wait_queue *wq; |
351 | struct qstr qstr; | 356 | struct qstr qstr; |
352 | char *name; | 357 | char *name; |
@@ -405,7 +410,7 @@ int autofs4_wait(struct autofs_sb_info *sbi, | |||
405 | return -EINTR; | 410 | return -EINTR; |
406 | } | 411 | } |
407 | 412 | ||
408 | ret = validate_request(&wq, sbi, &qstr, dentry, notify); | 413 | ret = validate_request(&wq, sbi, &qstr, path, notify); |
409 | if (ret <= 0) { | 414 | if (ret <= 0) { |
410 | if (ret != -EINTR) | 415 | if (ret != -EINTR) |
411 | mutex_unlock(&sbi->wq_mutex); | 416 | mutex_unlock(&sbi->wq_mutex); |
diff --git a/fs/dcache.c b/fs/dcache.c index 5c7cc953ac81..252378359a8f 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -1273,38 +1273,44 @@ rename_retry: | |||
1273 | goto again; | 1273 | goto again; |
1274 | } | 1274 | } |
1275 | 1275 | ||
1276 | /* | 1276 | struct check_mount { |
1277 | * Search for at least 1 mount point in the dentry's subdirs. | 1277 | struct vfsmount *mnt; |
1278 | * We descend to the next level whenever the d_subdirs | 1278 | unsigned int mounted; |
1279 | * list is non-empty and continue searching. | 1279 | }; |
1280 | */ | ||
1281 | 1280 | ||
1282 | static enum d_walk_ret check_mount(void *data, struct dentry *dentry) | 1281 | static enum d_walk_ret path_check_mount(void *data, struct dentry *dentry) |
1283 | { | 1282 | { |
1284 | int *ret = data; | 1283 | struct check_mount *info = data; |
1285 | if (d_mountpoint(dentry)) { | 1284 | struct path path = { .mnt = info->mnt, .dentry = dentry }; |
1286 | *ret = 1; | 1285 | |
1286 | if (likely(!d_mountpoint(dentry))) | ||
1287 | return D_WALK_CONTINUE; | ||
1288 | if (__path_is_mountpoint(&path)) { | ||
1289 | info->mounted = 1; | ||
1287 | return D_WALK_QUIT; | 1290 | return D_WALK_QUIT; |
1288 | } | 1291 | } |
1289 | return D_WALK_CONTINUE; | 1292 | return D_WALK_CONTINUE; |
1290 | } | 1293 | } |
1291 | 1294 | ||
1292 | /** | 1295 | /** |
1293 | * have_submounts - check for mounts over a dentry | 1296 | * path_has_submounts - check for mounts over a dentry in the |
1294 | * @parent: dentry to check. | 1297 | * current namespace. |
1298 | * @parent: path to check. | ||
1295 | * | 1299 | * |
1296 | * Return true if the parent or its subdirectories contain | 1300 | * Return true if the parent or its subdirectories contain |
1297 | * a mount point | 1301 | * a mount point in the current namespace. |
1298 | */ | 1302 | */ |
1299 | int have_submounts(struct dentry *parent) | 1303 | int path_has_submounts(const struct path *parent) |
1300 | { | 1304 | { |
1301 | int ret = 0; | 1305 | struct check_mount data = { .mnt = parent->mnt, .mounted = 0 }; |
1302 | 1306 | ||
1303 | d_walk(parent, &ret, check_mount, NULL); | 1307 | read_seqlock_excl(&mount_lock); |
1308 | d_walk(parent->dentry, &data, path_check_mount, NULL); | ||
1309 | read_sequnlock_excl(&mount_lock); | ||
1304 | 1310 | ||
1305 | return ret; | 1311 | return data.mounted; |
1306 | } | 1312 | } |
1307 | EXPORT_SYMBOL(have_submounts); | 1313 | EXPORT_SYMBOL(path_has_submounts); |
1308 | 1314 | ||
1309 | /* | 1315 | /* |
1310 | * Called by mount code to set a mountpoint and check if the mountpoint is | 1316 | * Called by mount code to set a mountpoint and check if the mountpoint is |
diff --git a/fs/mount.h b/fs/mount.h index d2e25d7b64b3..2c856fc47ae3 100644 --- a/fs/mount.h +++ b/fs/mount.h | |||
@@ -94,6 +94,12 @@ extern struct mount *__lookup_mnt_last(struct vfsmount *, struct dentry *); | |||
94 | extern int __legitimize_mnt(struct vfsmount *, unsigned); | 94 | extern int __legitimize_mnt(struct vfsmount *, unsigned); |
95 | extern bool legitimize_mnt(struct vfsmount *, unsigned); | 95 | extern bool legitimize_mnt(struct vfsmount *, unsigned); |
96 | 96 | ||
97 | static inline bool __path_is_mountpoint(const struct path *path) | ||
98 | { | ||
99 | struct mount *m = __lookup_mnt(path->mnt, path->dentry); | ||
100 | return m && likely(!(m->mnt.mnt_flags & MNT_SYNC_UMOUNT)); | ||
101 | } | ||
102 | |||
97 | extern void __detach_mounts(struct dentry *dentry); | 103 | extern void __detach_mounts(struct dentry *dentry); |
98 | 104 | ||
99 | static inline void detach_mounts(struct dentry *dentry) | 105 | static inline void detach_mounts(struct dentry *dentry) |
diff --git a/fs/namei.c b/fs/namei.c index 1c8f4386b03f..47781b0d909d 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -1200,7 +1200,7 @@ static int follow_managed(struct path *path, struct nameidata *nd) | |||
1200 | if (managed & DCACHE_MANAGE_TRANSIT) { | 1200 | if (managed & DCACHE_MANAGE_TRANSIT) { |
1201 | BUG_ON(!path->dentry->d_op); | 1201 | BUG_ON(!path->dentry->d_op); |
1202 | BUG_ON(!path->dentry->d_op->d_manage); | 1202 | BUG_ON(!path->dentry->d_op->d_manage); |
1203 | ret = path->dentry->d_op->d_manage(path->dentry, false); | 1203 | ret = path->dentry->d_op->d_manage(path, false); |
1204 | if (ret < 0) | 1204 | if (ret < 0) |
1205 | break; | 1205 | break; |
1206 | } | 1206 | } |
@@ -1263,10 +1263,10 @@ int follow_down_one(struct path *path) | |||
1263 | } | 1263 | } |
1264 | EXPORT_SYMBOL(follow_down_one); | 1264 | EXPORT_SYMBOL(follow_down_one); |
1265 | 1265 | ||
1266 | static inline int managed_dentry_rcu(struct dentry *dentry) | 1266 | static inline int managed_dentry_rcu(const struct path *path) |
1267 | { | 1267 | { |
1268 | return (dentry->d_flags & DCACHE_MANAGE_TRANSIT) ? | 1268 | return (path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) ? |
1269 | dentry->d_op->d_manage(dentry, true) : 0; | 1269 | path->dentry->d_op->d_manage(path, true) : 0; |
1270 | } | 1270 | } |
1271 | 1271 | ||
1272 | /* | 1272 | /* |
@@ -1282,7 +1282,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, | |||
1282 | * Don't forget we might have a non-mountpoint managed dentry | 1282 | * Don't forget we might have a non-mountpoint managed dentry |
1283 | * that wants to block transit. | 1283 | * that wants to block transit. |
1284 | */ | 1284 | */ |
1285 | switch (managed_dentry_rcu(path->dentry)) { | 1285 | switch (managed_dentry_rcu(path)) { |
1286 | case -ECHILD: | 1286 | case -ECHILD: |
1287 | default: | 1287 | default: |
1288 | return false; | 1288 | return false; |
@@ -1392,8 +1392,7 @@ int follow_down(struct path *path) | |||
1392 | if (managed & DCACHE_MANAGE_TRANSIT) { | 1392 | if (managed & DCACHE_MANAGE_TRANSIT) { |
1393 | BUG_ON(!path->dentry->d_op); | 1393 | BUG_ON(!path->dentry->d_op); |
1394 | BUG_ON(!path->dentry->d_op->d_manage); | 1394 | BUG_ON(!path->dentry->d_op->d_manage); |
1395 | ret = path->dentry->d_op->d_manage( | 1395 | ret = path->dentry->d_op->d_manage(path, false); |
1396 | path->dentry, false); | ||
1397 | if (ret < 0) | 1396 | if (ret < 0) |
1398 | return ret == -EISDIR ? 0 : ret; | 1397 | return ret == -EISDIR ? 0 : ret; |
1399 | } | 1398 | } |
diff --git a/fs/namespace.c b/fs/namespace.c index 9ad88a45b3e3..f7e28f8ea04d 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -1159,6 +1159,35 @@ struct vfsmount *mntget(struct vfsmount *mnt) | |||
1159 | } | 1159 | } |
1160 | EXPORT_SYMBOL(mntget); | 1160 | EXPORT_SYMBOL(mntget); |
1161 | 1161 | ||
1162 | /* path_is_mountpoint() - Check if path is a mount in the current | ||
1163 | * namespace. | ||
1164 | * | ||
1165 | * d_mountpoint() can only be used reliably to establish if a dentry is | ||
1166 | * not mounted in any namespace and that common case is handled inline. | ||
1167 | * d_mountpoint() isn't aware of the possibility there may be multiple | ||
1168 | * mounts using a given dentry in a different namespace. This function | ||
1169 | * checks if the passed in path is a mountpoint rather than the dentry | ||
1170 | * alone. | ||
1171 | */ | ||
1172 | bool path_is_mountpoint(const struct path *path) | ||
1173 | { | ||
1174 | unsigned seq; | ||
1175 | bool res; | ||
1176 | |||
1177 | if (!d_mountpoint(path->dentry)) | ||
1178 | return false; | ||
1179 | |||
1180 | rcu_read_lock(); | ||
1181 | do { | ||
1182 | seq = read_seqbegin(&mount_lock); | ||
1183 | res = __path_is_mountpoint(path); | ||
1184 | } while (read_seqretry(&mount_lock, seq)); | ||
1185 | rcu_read_unlock(); | ||
1186 | |||
1187 | return res; | ||
1188 | } | ||
1189 | EXPORT_SYMBOL(path_is_mountpoint); | ||
1190 | |||
1162 | struct vfsmount *mnt_clone_internal(const struct path *path) | 1191 | struct vfsmount *mnt_clone_internal(const struct path *path) |
1163 | { | 1192 | { |
1164 | struct mount *p; | 1193 | struct mount *p; |
diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 5beed7b30561..c965e4469499 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h | |||
@@ -139,7 +139,7 @@ struct dentry_operations { | |||
139 | void (*d_iput)(struct dentry *, struct inode *); | 139 | void (*d_iput)(struct dentry *, struct inode *); |
140 | char *(*d_dname)(struct dentry *, char *, int); | 140 | char *(*d_dname)(struct dentry *, char *, int); |
141 | struct vfsmount *(*d_automount)(struct path *); | 141 | struct vfsmount *(*d_automount)(struct path *); |
142 | int (*d_manage)(struct dentry *, bool); | 142 | int (*d_manage)(const struct path *, bool); |
143 | struct dentry *(*d_real)(struct dentry *, const struct inode *, | 143 | struct dentry *(*d_real)(struct dentry *, const struct inode *, |
144 | unsigned int); | 144 | unsigned int); |
145 | } ____cacheline_aligned; | 145 | } ____cacheline_aligned; |
@@ -254,7 +254,7 @@ extern struct dentry *d_find_alias(struct inode *); | |||
254 | extern void d_prune_aliases(struct inode *); | 254 | extern void d_prune_aliases(struct inode *); |
255 | 255 | ||
256 | /* test whether we have any submounts in a subdir tree */ | 256 | /* test whether we have any submounts in a subdir tree */ |
257 | extern int have_submounts(struct dentry *); | 257 | extern int path_has_submounts(const struct path *); |
258 | 258 | ||
259 | /* | 259 | /* |
260 | * This adds the entry to the hash queues. | 260 | * This adds the entry to the hash queues. |
diff --git a/include/linux/mount.h b/include/linux/mount.h index cf2b5784b649..c6f55158d5e5 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h | |||
@@ -98,4 +98,6 @@ extern dev_t name_to_dev_t(const char *name); | |||
98 | 98 | ||
99 | extern unsigned int sysctl_mount_max; | 99 | extern unsigned int sysctl_mount_max; |
100 | 100 | ||
101 | extern bool path_is_mountpoint(const struct path *path); | ||
102 | |||
101 | #endif /* _LINUX_MOUNT_H */ | 103 | #endif /* _LINUX_MOUNT_H */ |