diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index aea71af52107..9eecc0992408 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -1359,6 +1359,28 @@ vn_fullpath1(struct thread *td, struct vnode *vp, struct vnode *rdir, return (0); } +struct vnode * +vn_dir_dd_ino(struct vnode *vp) +{ + struct namecache *ncp; + struct vnode *ddvp; + + ASSERT_VOP_LOCKED(vp, "vn_dir_dd_ino"); + CACHE_RLOCK(); + TAILQ_FOREACH(ncp, &(vp->v_cache_dst), nc_dst) { + if ((ncp->nc_flag & NCF_ISDOTDOT) != 0) + continue; + ddvp = ncp->nc_dvp; + VI_LOCK(ddvp); + CACHE_RUNLOCK(); + if (vget(ddvp, LK_INTERLOCK | LK_SHARED | LK_NOWAIT, curthread)) + return (NULL); + return (ddvp); + } + CACHE_RUNLOCK(); + return (NULL); +} + int vn_commname(struct vnode *vp, char *buf, u_int buflen) { diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index bc16acff9007..42bfb65092a0 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -622,6 +622,8 @@ int vn_fullpath(struct thread *td, struct vnode *vn, char **retbuf, char **freebuf); int vn_fullpath_global(struct thread *td, struct vnode *vn, char **retbuf, char **freebuf); +struct vnode * + vn_dir_dd_ino(struct vnode *vp); int vn_commname(struct vnode *vn, char *buf, u_int buflen); int vn_path_to_global_path(struct thread *td, struct vnode *vp, char *path, u_int pathlen); diff --git a/sys/ufs/ufs/ufs_lookup.c b/sys/ufs/ufs/ufs_lookup.c index 02fda9f18642..70685659633f 100644 --- a/sys/ufs/ufs/ufs_lookup.c +++ b/sys/ufs/ufs/ufs_lookup.c @@ -1387,13 +1387,29 @@ ufs_dirempty(ip, parentino, cred) } static int -ufs_dir_dd_ino(struct vnode *vp, struct ucred *cred, ino_t *dd_ino) +ufs_dir_dd_ino(struct vnode *vp, struct ucred *cred, ino_t *dd_ino, + struct vnode **dd_vp) { struct dirtemplate dirbuf; + struct vnode *ddvp; int error, namlen; + ASSERT_VOP_LOCKED(vp, "ufs_dir_dd_ino"); if (vp->v_type != VDIR) return (ENOTDIR); + /* + * First check to see if we have it in the name cache. + */ + if ((ddvp = vn_dir_dd_ino(vp)) != NULL) { + KASSERT(ddvp->v_mount == vp->v_mount, + ("ufs_dir_dd_ino: Unexpected mount point crossing")); + *dd_ino = VTOI(ddvp)->i_number; + *dd_vp = ddvp; + return (0); + } + /* + * Have to read the directory. + */ error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, NULL, NULL); @@ -1411,6 +1427,7 @@ ufs_dir_dd_ino(struct vnode *vp, struct ucred *cred, ino_t *dd_ino) dirbuf.dotdot_name[1] != '.') return (ENOTDIR); *dd_ino = dirbuf.dotdot_ino; + *dd_vp = NULL; return (0); } @@ -1435,7 +1452,7 @@ ufs_checkpath(ino_t source_ino, ino_t parent_ino, struct inode *target, struct u if (target->i_number == ROOTINO) return (0); for (;;) { - error = ufs_dir_dd_ino(vp, cred, &dd_ino); + error = ufs_dir_dd_ino(vp, cred, &dd_ino, &vp1); if (error != 0) break; if (dd_ino == source_ino) { @@ -1446,22 +1463,16 @@ ufs_checkpath(ino_t source_ino, ino_t parent_ino, struct inode *target, struct u break; if (dd_ino == parent_ino) break; - error = VFS_VGET(mp, dd_ino, LK_SHARED | LK_NOWAIT, &vp1); - if (error != 0) { - *wait_ino = dd_ino; - break; - } - /* Recheck that ".." still points to vp1 after relock of vp */ - error = ufs_dir_dd_ino(vp, cred, &dd_ino); - if (error != 0) { - vput(vp1); - break; - } - /* Redo the check of ".." if directory was reparented */ - if (dd_ino != VTOI(vp1)->i_number) { - vput(vp1); - continue; + if (vp1 == NULL) { + error = VFS_VGET(mp, dd_ino, LK_SHARED | LK_NOWAIT, + &vp1); + if (error != 0) { + *wait_ino = dd_ino; + break; + } } + KASSERT(dd_ino == VTOI(vp1)->i_number, + ("directory %d reparented\n", VTOI(vp1)->i_number)); if (vp != tvp) vput(vp); vp = vp1; @@ -1469,6 +1480,8 @@ ufs_checkpath(ino_t source_ino, ino_t parent_ino, struct inode *target, struct u if (error == ENOTDIR) panic("checkpath: .. not a directory\n"); + if (vp1 != NULL) + vput(vp1); if (vp != tvp) vput(vp); return (error);