From owner-svn-src-head@FreeBSD.ORG Sun Mar 8 19:05:54 2009 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id BBD881065670; Sun, 8 Mar 2009 19:05:54 +0000 (UTC) (envelope-from marcus@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 9EF038FC08; Sun, 8 Mar 2009 19:05:54 +0000 (UTC) (envelope-from marcus@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id n28J5sXM092476; Sun, 8 Mar 2009 19:05:54 GMT (envelope-from marcus@svn.freebsd.org) Received: (from marcus@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id n28J5sFQ092475; Sun, 8 Mar 2009 19:05:54 GMT (envelope-from marcus@svn.freebsd.org) Message-Id: <200903081905.n28J5sFQ092475@svn.freebsd.org> From: Joe Marcus Clarke Date: Sun, 8 Mar 2009 19:05:54 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r189539 - head/sys/kern X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 08 Mar 2009 19:05:55 -0000 Author: marcus (doc,ports committer) Date: Sun Mar 8 19:05:53 2009 New Revision: 189539 URL: http://svn.freebsd.org/changeset/base/189539 Log: Add a default implementation for VOP_VPTOCNP(9) which scans the parent directory of a vnode to find a dirent with a matching file number. The name from that dirent is then used to provide the component name. Note: if the initial vnode argument is not a directory itself, then the default VOP_VPTOCNP(9) implementation still returns ENOENT. Reviewed by: kib Approved by: kib Tested by: pho Modified: head/sys/kern/vfs_default.c Modified: head/sys/kern/vfs_default.c ============================================================================== --- head/sys/kern/vfs_default.c Sun Mar 8 18:02:30 2009 (r189538) +++ head/sys/kern/vfs_default.c Sun Mar 8 19:05:53 2009 (r189539) @@ -48,8 +48,11 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include #include #include +#include #include #include @@ -63,6 +66,14 @@ __FBSDID("$FreeBSD$"); static int vop_nolookup(struct vop_lookup_args *); static int vop_nostrategy(struct vop_strategy_args *); +static int get_next_dirent(struct vnode *vp, struct dirent **dpp, + char *dirbuf, int dirbuflen, off_t *off, + char **cpos, int *len, int *eofflag, + struct thread *td); +static int dirent_exists(struct vnode *vp, const char *dirname, + struct thread *td); + +#define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN+1) + 4) /* * This vnode table stores what we want to do if the filesystem doesn't @@ -98,7 +109,7 @@ struct vop_vector default_vnodeops = { .vop_revoke = VOP_PANIC, .vop_strategy = vop_nostrategy, .vop_unlock = vop_stdunlock, - .vop_vptocnp = VOP_ENOENT, + .vop_vptocnp = vop_stdvptocnp, .vop_vptofh = vop_stdvptofh, }; @@ -210,6 +221,108 @@ vop_nostrategy (struct vop_strategy_args return (EOPNOTSUPP); } +static int +get_next_dirent(struct vnode *vp, struct dirent **dpp, char *dirbuf, + int dirbuflen, off_t *off, char **cpos, int *len, + int *eofflag, struct thread *td) +{ + int error, reclen; + struct uio uio; + struct iovec iov; + struct dirent *dp; + + KASSERT(VOP_ISLOCKED(vp), ("vp %p is not locked", vp)); + KASSERT(vp->v_type == VDIR, ("vp %p is not a directory", vp)); + + if (*len == 0) { + iov.iov_base = dirbuf; + iov.iov_len = dirbuflen; + + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = *off; + uio.uio_resid = dirbuflen; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_READ; + uio.uio_td = td; + + *eofflag = 0; + +#ifdef MAC + error = mac_vnode_check_readdir(td->td_ucred, vp); + if (error == 0) +#endif + error = VOP_READDIR(vp, &uio, td->td_ucred, eofflag, + NULL, NULL); + if (error) + return (error); + + *off = uio.uio_offset; + + *cpos = dirbuf; + *len = (dirbuflen - uio.uio_resid); + } + + dp = (struct dirent *)(*cpos); + reclen = dp->d_reclen; + *dpp = dp; + + /* check for malformed directory.. */ + if (reclen < DIRENT_MINSIZE) + return (EINVAL); + + *cpos += reclen; + *len -= reclen; + + return (0); +} + +/* + * Check if a named file exists in a given directory vnode. + */ +static int +dirent_exists(struct vnode *vp, const char *dirname, struct thread *td) +{ + char *dirbuf, *cpos; + int error, eofflag, dirbuflen, len, found; + off_t off; + struct dirent *dp; + struct vattr va; + + KASSERT(VOP_ISLOCKED(vp), ("vp %p is not locked", vp)); + KASSERT(vp->v_type == VDIR, ("vp %p is not a directory", vp)); + + found = 0; + + error = VOP_GETATTR(vp, &va, td->td_ucred); + if (error) + return (found); + + dirbuflen = DEV_BSIZE; + if (dirbuflen < va.va_blocksize) + dirbuflen = va.va_blocksize; + dirbuf = (char *)malloc(dirbuflen, M_TEMP, M_WAITOK); + + off = 0; + len = 0; + do { + error = get_next_dirent(vp, &dp, dirbuf, dirbuflen, &off, + &cpos, &len, &eofflag, td); + if (error) + goto out; + + if ((dp->d_type != DT_WHT) && + !strcmp(dp->d_name, dirname)) { + found = 1; + goto out; + } + } while (len > 0 || !eofflag); + +out: + free(dirbuf, M_TEMP); + return (found); +} + /* * Advisory record locking support */ @@ -557,6 +670,127 @@ vop_stdvptofh(struct vop_vptofh_args *ap return (EOPNOTSUPP); } +int +vop_stdvptocnp(struct vop_vptocnp_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct vnode **dvp = ap->a_vpp; + char *buf = ap->a_buf; + int *buflen = ap->a_buflen; + char *dirbuf, *cpos; + int i, error, eofflag, dirbuflen, flags, locked, len, covered; + off_t off; + ino_t fileno; + struct vattr va; + struct nameidata nd; + struct thread *td; + struct dirent *dp; + struct vnode *mvp; + + i = *buflen; + error = 0; + covered = 0; + td = curthread; + + if (vp->v_type != VDIR) + return (ENOENT); + + error = VOP_GETATTR(vp, &va, td->td_ucred); + if (error) + return (error); + + VREF(vp); + locked = VOP_ISLOCKED(vp); + VOP_UNLOCK(vp, 0); + NDINIT_ATVP(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, + "..", vp, td); + flags = FREAD; + error = vn_open(&nd, &flags, 0, NULL); + if (error) { + vn_lock(vp, locked | LK_RETRY); + return (error); + } + NDFREE(&nd, NDF_ONLY_PNBUF); + + mvp = *dvp = nd.ni_vp; + + if (vp->v_mount != (*dvp)->v_mount && + ((*dvp)->v_vflag & VV_ROOT) && + ((*dvp)->v_mount->mnt_flag & MNT_UNION)) { + *dvp = (*dvp)->v_mount->mnt_vnodecovered; + VREF(mvp); + VOP_UNLOCK(mvp, 0); + vn_close(mvp, FREAD, td->td_ucred, td); + VREF(*dvp); + vn_lock(*dvp, LK_EXCLUSIVE | LK_RETRY); + covered = 1; + } + + fileno = va.va_fileid; + + dirbuflen = DEV_BSIZE; + if (dirbuflen < va.va_blocksize) + dirbuflen = va.va_blocksize; + dirbuf = (char *)malloc(dirbuflen, M_TEMP, M_WAITOK); + + if ((*dvp)->v_type != VDIR) { + error = ENOENT; + goto out; + } + + off = 0; + len = 0; + do { + /* call VOP_READDIR of parent */ + error = get_next_dirent(*dvp, &dp, dirbuf, dirbuflen, &off, + &cpos, &len, &eofflag, td); + if (error) + goto out; + + if ((dp->d_type != DT_WHT) && + (dp->d_fileno == fileno)) { + if (covered) { + VOP_UNLOCK(*dvp, 0); + vn_lock(mvp, LK_EXCLUSIVE | LK_RETRY); + if (dirent_exists(mvp, dp->d_name, td)) { + error = ENOENT; + VOP_UNLOCK(mvp, 0); + vn_lock(*dvp, LK_EXCLUSIVE | LK_RETRY); + goto out; + } + VOP_UNLOCK(mvp, 0); + vn_lock(*dvp, LK_EXCLUSIVE | LK_RETRY); + } + i -= dp->d_namlen; + + if (i < 0) { + error = ENOMEM; + goto out; + } + bcopy(dp->d_name, buf + i, dp->d_namlen); + error = 0; + goto out; + } + } while (len > 0 || !eofflag); + error = ENOENT; + +out: + free(dirbuf, M_TEMP); + if (!error) { + *buflen = i; + vhold(*dvp); + } + if (covered) { + vput(*dvp); + vrele(mvp); + } else { + VOP_UNLOCK(mvp, 0); + vn_close(mvp, FREAD, td->td_ucred, td); + } + vn_lock(vp, locked | LK_RETRY); + return (error); +} + /* * vfs default ops * used to fill the vfs function table to get reasonable default return values.