reimplement path_mountpoint() with less magic

... and get rid of a bunch of bugs in it. Background:
the reason for path_mountpoint() is that umount() really doesn't
want attempts to revalidate the root of what it's trying to umount.
The thing we want to avoid actually happen from complete_walk();
solution was to do something parallel to normal path_lookupat()
and it both went overboard and got the boilerplate subtly
(and not so subtly) wrong.

A better solution is to do pretty much what the normal path_lookupat()
does, but instead of complete_walk() do unlazy_walk(). All it takes
to avoid that ->d_weak_revalidate() call... mountpoint_last() goes
away, along with everything it got wrong, and so does the magic around
LOOKUP_NO_REVAL.

Another source of bugs is that when we traverse mounts at the final
location (and we need to do that - umount . expects to get whatever's
overmounting ., if any, out of the lookup) we really ought to take
care of ->d_manage() - as it is, manual umount of autofs automount
in progress can lead to unpleasant surprises for the daemon. Easily
solved by using handle_lookup_down() instead of follow_mount().

Tested-by: Ian Kent <raven@themaw.net>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Al Viro c64cd6e3 1edc8eb2

Changed files
+12 -80
fs
include
linux
+12 -77
fs/namei.c
··· 1649 1649 if (IS_ERR(dentry)) 1650 1650 return dentry; 1651 1651 if (unlikely(!d_in_lookup(dentry))) { 1652 - if (!(flags & LOOKUP_NO_REVAL)) { 1653 - int error = d_revalidate(dentry, flags); 1654 - if (unlikely(error <= 0)) { 1655 - if (!error) { 1656 - d_invalidate(dentry); 1657 - dput(dentry); 1658 - goto again; 1659 - } 1652 + int error = d_revalidate(dentry, flags); 1653 + if (unlikely(error <= 0)) { 1654 + if (!error) { 1655 + d_invalidate(dentry); 1660 1656 dput(dentry); 1661 - dentry = ERR_PTR(error); 1657 + goto again; 1662 1658 } 1659 + dput(dentry); 1660 + dentry = ERR_PTR(error); 1663 1661 } 1664 1662 } else { 1665 1663 old = inode->i_op->lookup(inode, dentry, flags); ··· 2616 2618 EXPORT_SYMBOL(user_path_at_empty); 2617 2619 2618 2620 /** 2619 - * mountpoint_last - look up last component for umount 2620 - * @nd: pathwalk nameidata - currently pointing at parent directory of "last" 2621 - * 2622 - * This is a special lookup_last function just for umount. In this case, we 2623 - * need to resolve the path without doing any revalidation. 2624 - * 2625 - * The nameidata should be the result of doing a LOOKUP_PARENT pathwalk. Since 2626 - * mountpoints are always pinned in the dcache, their ancestors are too. Thus, 2627 - * in almost all cases, this lookup will be served out of the dcache. The only 2628 - * cases where it won't are if nd->last refers to a symlink or the path is 2629 - * bogus and it doesn't exist. 2630 - * 2631 - * Returns: 2632 - * -error: if there was an error during lookup. This includes -ENOENT if the 2633 - * lookup found a negative dentry. 2634 - * 2635 - * 0: if we successfully resolved nd->last and found it to not to be a 2636 - * symlink that needs to be followed. 2637 - * 2638 - * 1: if we successfully resolved nd->last and found it to be a symlink 2639 - * that needs to be followed. 2640 - */ 2641 - static int 2642 - mountpoint_last(struct nameidata *nd) 2643 - { 2644 - int error = 0; 2645 - struct dentry *dir = nd->path.dentry; 2646 - struct path path; 2647 - 2648 - /* If we're in rcuwalk, drop out of it to handle last component */ 2649 - if (nd->flags & LOOKUP_RCU) { 2650 - if (unlazy_walk(nd)) 2651 - return -ECHILD; 2652 - } 2653 - 2654 - nd->flags &= ~LOOKUP_PARENT; 2655 - 2656 - if (unlikely(nd->last_type != LAST_NORM)) { 2657 - error = handle_dots(nd, nd->last_type); 2658 - if (error) 2659 - return error; 2660 - path.dentry = dget(nd->path.dentry); 2661 - } else { 2662 - path.dentry = d_lookup(dir, &nd->last); 2663 - if (!path.dentry) { 2664 - /* 2665 - * No cached dentry. Mounted dentries are pinned in the 2666 - * cache, so that means that this dentry is probably 2667 - * a symlink or the path doesn't actually point 2668 - * to a mounted dentry. 2669 - */ 2670 - path.dentry = lookup_slow(&nd->last, dir, 2671 - nd->flags | LOOKUP_NO_REVAL); 2672 - if (IS_ERR(path.dentry)) 2673 - return PTR_ERR(path.dentry); 2674 - } 2675 - } 2676 - if (d_flags_negative(smp_load_acquire(&path.dentry->d_flags))) { 2677 - dput(path.dentry); 2678 - return -ENOENT; 2679 - } 2680 - path.mnt = nd->path.mnt; 2681 - return step_into(nd, &path, 0, d_backing_inode(path.dentry), 0); 2682 - } 2683 - 2684 - /** 2685 2621 * path_mountpoint - look up a path to be umounted 2686 2622 * @nd: lookup context 2687 2623 * @flags: lookup flags ··· 2631 2699 int err; 2632 2700 2633 2701 while (!(err = link_path_walk(s, nd)) && 2634 - (err = mountpoint_last(nd)) > 0) { 2702 + (err = lookup_last(nd)) > 0) { 2635 2703 s = trailing_symlink(nd); 2636 2704 } 2705 + if (!err && (nd->flags & LOOKUP_RCU)) 2706 + err = unlazy_walk(nd); 2707 + if (!err) 2708 + err = handle_lookup_down(nd); 2637 2709 if (!err) { 2638 2710 *path = nd->path; 2639 2711 nd->path.mnt = NULL; 2640 2712 nd->path.dentry = NULL; 2641 - follow_mount(path); 2642 2713 } 2643 2714 terminate_walk(nd); 2644 2715 return err;
-2
fs/nfs/nfstrace.h
··· 206 206 TRACE_DEFINE_ENUM(LOOKUP_PARENT); 207 207 TRACE_DEFINE_ENUM(LOOKUP_REVAL); 208 208 TRACE_DEFINE_ENUM(LOOKUP_RCU); 209 - TRACE_DEFINE_ENUM(LOOKUP_NO_REVAL); 210 209 TRACE_DEFINE_ENUM(LOOKUP_OPEN); 211 210 TRACE_DEFINE_ENUM(LOOKUP_CREATE); 212 211 TRACE_DEFINE_ENUM(LOOKUP_EXCL); ··· 223 224 { LOOKUP_PARENT, "PARENT" }, \ 224 225 { LOOKUP_REVAL, "REVAL" }, \ 225 226 { LOOKUP_RCU, "RCU" }, \ 226 - { LOOKUP_NO_REVAL, "NO_REVAL" }, \ 227 227 { LOOKUP_OPEN, "OPEN" }, \ 228 228 { LOOKUP_CREATE, "CREATE" }, \ 229 229 { LOOKUP_EXCL, "EXCL" }, \
-1
include/linux/namei.h
··· 34 34 35 35 /* internal use only */ 36 36 #define LOOKUP_PARENT 0x0010 37 - #define LOOKUP_NO_REVAL 0x0080 38 37 #define LOOKUP_JUMPED 0x1000 39 38 #define LOOKUP_ROOT 0x2000 40 39 #define LOOKUP_ROOT_GRABBED 0x0008