History

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().

Al Viro committed 6 years ago

Commit c64cd6e1 parent 1edc8eb3 files · +12 −80Browse files

Unified · Split

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