diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index 03759ff3fc42..2ad95540f386 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -1,4 +1,4 @@ -/* $NetBSD: vfs_cache.c,v 1.139 2020/04/13 19:23:18 ad Exp $ */ +/* $NetBSD: vfs_cache.c,v 1.140 2020/04/22 21:35:52 ad Exp $ */ /*- * Copyright (c) 2008, 2019, 2020 The NetBSD Foundation, Inc. @@ -172,7 +172,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: vfs_cache.c,v 1.139 2020/04/13 19:23:18 ad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: vfs_cache.c,v 1.140 2020/04/22 21:35:52 ad Exp $"); #define __NAMECACHE_PRIVATE #ifdef _KERNEL_OPT @@ -664,10 +664,17 @@ cache_lookup_linked(struct vnode *dvp, const char *name, size_t namelen, * before we get its lock. * * Note that the two locks can be the same if looking up a dot, for - * example: /usr/bin/. + * example: /usr/bin/. If looking up the parent (..) we can't wait + * on the lock as child -> parent is the wrong direction. */ if (*plock != &dvi->vi_nc_lock) { - rw_enter(&dvi->vi_nc_lock, RW_READER); + if (namelen == 2 && name[0] == '.' && name[1] == '.') { + if (!rw_tryenter(&dvi->vi_nc_lock, RW_READER)) { + return false; + } + } else { + rw_enter(&dvi->vi_nc_lock, RW_READER); + } if (*plock != NULL) { rw_exit(*plock); } diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c index 83432adf7185..9046f3bb8aa1 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -1,4 +1,4 @@ -/* $NetBSD: vfs_lookup.c,v 1.218 2020/04/21 21:42:47 ad Exp $ */ +/* $NetBSD: vfs_lookup.c,v 1.219 2020/04/22 21:35:52 ad Exp $ */ /* * Copyright (c) 1982, 1986, 1989, 1993 @@ -37,7 +37,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: vfs_lookup.c,v 1.218 2020/04/21 21:42:47 ad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: vfs_lookup.c,v 1.219 2020/04/22 21:35:52 ad Exp $"); #ifdef _KERNEL_OPT #include "opt_magiclinks.h" @@ -1286,14 +1286,19 @@ lookup_fastforward(struct namei_state *state, struct vnode **searchdir_ret, } /* - * Can't deal with dotdot lookups, because it means lock - * order reversal, and there are checks in lookup_once() - * that need to be made. Also check for missing mountpoints. + * Can't deal with DOTDOT lookups if NOCROSSMOUNT or the + * lookup is chrooted. */ - if ((cnp->cn_flags & ISDOTDOT) != 0 || - searchdir->v_mount == NULL) { - error = EOPNOTSUPP; - break; + if ((cnp->cn_flags & ISDOTDOT) != 0) { + if ((searchdir->v_vflag & VV_ROOT) != 0 && + (cnp->cn_flags & NOCROSSMOUNT)) { + error = EOPNOTSUPP; + break; + } + if (ndp->ni_rootdir != rootvnode) { + error = EOPNOTSUPP; + break; + } } /* @@ -1309,13 +1314,6 @@ lookup_fastforward(struct namei_state *state, struct vnode **searchdir_ret, } } - /* Can't deal with -o union lookups. */ - if ((searchdir->v_vflag & VV_ROOT) != 0 && - (searchdir->v_mount->mnt_flag & MNT_UNION) != 0) { - error = EOPNOTSUPP; - break; - } - /* * Good, now look for it in cache. cache_lookup_linked() * will fail if there's nothing there, or if there's no @@ -1329,9 +1327,18 @@ lookup_fastforward(struct namei_state *state, struct vnode **searchdir_ret, } KASSERT(plock != NULL && rw_lock_held(plock)); - /* Scored a hit. Negative is good too (ENOENT). */ + /* + * Scored a hit. Negative is good too (ENOENT). If there's + * a '-o union' mount here, punt and let lookup_once() deal + * with it. + */ if (foundobj == NULL) { - error = ENOENT; + if ((searchdir->v_vflag & VV_ROOT) != 0 && + (searchdir->v_mount->mnt_flag & MNT_UNION) != 0) { + error = EOPNOTSUPP; + } else { + error = ENOENT; + } break; }