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 <sys/malloc.h>
 #include <sys/mount.h>
 #include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/fcntl.h>
 #include <sys/unistd.h>
 #include <sys/vnode.h>
+#include <sys/dirent.h>
 #include <sys/poll.h>
 
 #include <vm/vm.h>
@@ -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.
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to