--- //depot/projects/trustedbsd/base/sys/kern/vfs_cache.c 2002/06/28 20:54:03 +++ //depot/projects/trustedbsd/mac/sys/kern/vfs_cache.c 2002/06/28 21:44:36 @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -793,106 +794,202 @@ * Thus begins the fullpath magic. */ -#undef STATNODE -#define STATNODE(name) \ - static u_int name; \ - SYSCTL_UINT(_vfs_cache, OID_AUTO, name, CTLFLAG_RD, &name, 0, "") - static int disablefullpath; SYSCTL_INT(_debug, OID_AUTO, disablefullpath, CTLFLAG_RW, &disablefullpath, 0, "Disable the vn_fullpath function"); -STATNODE(numfullpathcalls); -STATNODE(numfullpathfail1); -STATNODE(numfullpathfail2); -STATNODE(numfullpathfail3); -STATNODE(numfullpathfail4); -STATNODE(numfullpathfound); +static int +vn_fullpath_dirents_searchbyid(struct thread *td, struct vnode *dvp, + struct dirent *dp, struct dirent *enddp, const struct vattr *vap, + struct dirent **retdp) +{ + struct vattr pvattr; + struct componentname cnp; + struct vnode *vp; + struct ucred *ucred = td->td_ucred; + int error; + + *retdp = NULL; + for (; dp != enddp; dp = (struct dirent *)((char *)dp + dp->d_reclen)) { + if (dp->d_name[0] == '.' && (dp->d_namlen == 1 || + (dp->d_namlen == 2 && dp->d_name[1] == '.'))) + continue; + cnp.cn_nameiop = LOOKUP; + cnp.cn_flags = LOCKPARENT | ISLASTCN | NOFOLLOW; + cnp.cn_thread = td; + cnp.cn_cred = ucred; + cnp.cn_nameptr = dp->d_name; + cnp.cn_namelen = dp->d_namlen; + error = VOP_LOOKUP(dvp, &vp, &cnp); + if (error) + return (error); + error = VOP_GETATTR(vp, &pvattr, ucred, td); + if (vp != dvp) + (void)vput(vp); + else + vrele(vp); /* if looking up "." */ + if (error) + return (error); + if (pvattr.va_fsid == vap->va_fsid && + pvattr.va_fileid == vap->va_fileid) { + *retdp = dp; + break; + } + } + return (0); +} -/* - * Retrieve the full filesystem path that correspond to a vnode from the name - * cache (if available) - */ int -vn_fullpath(struct thread *td, struct vnode *vn, char **retbuf, char **freebuf) -{ - char *bp, *buf; - int i, slash_prefixed; - struct filedesc *fdp; - struct namecache *ncp; - struct vnode *vp; +vn_fullpath(struct thread *td, struct vnode *startdvp, struct vnode *startvp, + char **buf, char **freebuf) { + struct vattr cvattr; + struct vnode *vp, *dvp, *fd_rdir; + char *bp, *allocedmem, *direntmem; + const int direntmem_size = (32 << 10) - MAXPATHLEN; + int error, i, slash_prefixed; - numfullpathcalls++; if (disablefullpath) - return (ENODEV); - if (vn == NULL) - return (EINVAL); - buf = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); - bp = buf + MAXPATHLEN - 1; + return (EPERM); + FILEDESC_LOCK(td->td_proc->p_fd); + fd_rdir = td->td_proc->p_fd->fd_rdir; + vref(fd_rdir); + FILEDESC_UNLOCK(td->td_proc->p_fd); + allocedmem = malloc(MAXPATHLEN + direntmem_size, M_TEMP, M_WAITOK); + bp = allocedmem; + bp += MAXPATHLEN - 1; *bp = '\0'; - fdp = td->td_proc->p_fd; + direntmem = allocedmem + MAXPATHLEN; slash_prefixed = 0; - FILEDESC_LOCK(fdp); - for (vp = vn; vp != fdp->fd_rdir && vp != rootvnode;) { + vref(startvp); + for (vp = startvp;; vrele(vp), vp = dvp) { + struct iovec diov = { + direntmem, + direntmem_size + }; + struct uio duio = { + &diov, + 1, + 0, + direntmem_size, + UIO_SYSSPACE, + UIO_READ, + td + }; + struct dirent *dp; + int direof; + + if (vp == rootvnode || vp == fd_rdir) + break; if (vp->v_flag & VROOT) { if (vp->v_mount == NULL) { /* forced unmount */ - FILEDESC_UNLOCK(fdp); - free(buf, M_TEMP); - return (EBADF); + error = EBADF; + goto out; } - vp = vp->v_mount->mnt_vnodecovered; + dvp = vp->v_mount->mnt_vnodecovered; + vref(dvp); continue; } - if (vp != vn && vp->v_dd->v_id != vp->v_ddid) { - FILEDESC_UNLOCK(fdp); - numfullpathfail1++; - free(buf, M_TEMP); - return (ENOTDIR); + if (startdvp != NULL) { + dvp = startdvp; + vref(dvp); + startdvp = NULL; + } else { + if (vp->v_ddid == vp->v_dd->v_id) { + dvp = vp->v_dd; + vref(dvp); + } else { + struct componentname cnp; + + if (vp->v_type != VDIR) { + error = EBADF; + goto out; + } + cnp.cn_nameiop = LOOKUP; + cnp.cn_flags = 0; + cnp.cn_thread = td; + cnp.cn_cred = td->td_ucred; + cnp.cn_nameptr = ".."; + cnp.cn_namelen = 2; + vn_lock(vp, LK_EXCLUSIVE, td); + error = VOP_LOOKUP(vp, &dvp, &cnp); + if (error) { + VOP_UNLOCK(vp, 0, td); + goto out; + } + VOP_UNLOCK(dvp, 0, td); + } } - ncp = TAILQ_FIRST(&vp->v_cache_dst); - if (!ncp) { - FILEDESC_UNLOCK(fdp); - numfullpathfail2++; - free(buf, M_TEMP); - return (ENOENT); + if (vp == dvp) + break; + /* + * Utilize POSIX requirement of files having same + * st_dev and st_ino to be the same file, in our + * case with vattr.va_fsid and vattr.va_fileid. + */ + vn_lock(vp, LK_EXCLUSIVE, td); + error = VOP_GETATTR(vp, &cvattr, curthread->td_ucred, + curthread); + VOP_UNLOCK(vp, 0, td); + if (error) { + vrele(dvp); + goto out; } - if (vp != vn && ncp->nc_dvp != vp->v_dd) { - FILEDESC_UNLOCK(fdp); - numfullpathfail3++; - free(buf, M_TEMP); - return (EBADF); - } - for (i = ncp->nc_nlen - 1; i >= 0; i--) { - if (bp == buf) { - FILEDESC_UNLOCK(fdp); - numfullpathfail4++; - free(buf, M_TEMP); - return (ENOMEM); + vn_lock(dvp, LK_EXCLUSIVE, td); + for (direof = 0; !direof;) { + error = VOP_READDIR(dvp, &duio, td->td_ucred, &direof, + NULL, NULL); + if (error) + break; + error = vn_fullpath_dirents_searchbyid(td, dvp, + (struct dirent *)direntmem, + (struct dirent *)(direntmem + + direntmem_size - duio.uio_resid), + &cvattr, &dp); + if (error) + break; + if (dp != NULL) { + for (i = dp->d_namlen - 1; i >= 0; i--) { + if (bp == allocedmem) { + error = ENOMEM; + vput(dvp); + goto out; + } + *--bp = dp->d_name[i]; + } + goto nextcomp; } - *--bp = ncp->nc_name[i]; + diov.iov_base = direntmem; + diov.iov_len = direntmem_size; + duio.uio_resid = direntmem_size; } - if (bp == buf) { - FILEDESC_UNLOCK(fdp); - numfullpathfail4++; - free(buf, M_TEMP); - return (ENOMEM); + vput(dvp); + if (direof) + error = ENOENT; + goto out; +nextcomp: + if (bp == allocedmem) { + error = ENOMEM; + vput(dvp); + goto out; } *--bp = '/'; slash_prefixed = 1; - vp = ncp->nc_dvp; + VOP_UNLOCK(dvp, 0, td); } if (!slash_prefixed) { - if (bp == buf) { - FILEDESC_UNLOCK(fdp); - numfullpathfail4++; - free(buf, M_TEMP); - return (ENOMEM); + if (bp == allocedmem) { + error = ENOMEM; + goto out; } *--bp = '/'; } - FILEDESC_UNLOCK(fdp); - numfullpathfound++; - *retbuf = bp; - *freebuf = buf; - return (0); + error = 0; + *buf = bp; + *freebuf = allocedmem; +out: + vrele(vp); + vrele(fd_rdir); + if (error) + free(allocedmem, M_TEMP); + return (error); }