+void vnode_set_hard_link(vnode_t vp)
+{
+ vnode_lock(vp);
+
+ /*
+ * In theory, we're changing the vnode's identity as far as the
+ * name cache is concerned, so we ought to grab the name cache lock
+ * here. However, there is already a race, and grabbing the name
+ * cache lock only makes the race window slightly smaller.
+ *
+ * The race happens because the vnode already exists in the name
+ * cache, and could be found by one thread before another thread
+ * can set the hard link flag.
+ */
+
+ vp->v_flag |= VISHARDLINK;
+
+ vnode_unlock(vp);
+}
+
+
+void vnode_uncache_credentials(vnode_t vp)
+{
+ kauth_cred_t ucred = NULL;
+
+ if (vp->v_cred) {
+ vnode_lock(vp);
+
+ ucred = vp->v_cred;
+ vp->v_cred = NULL;
+
+ vnode_unlock(vp);
+
+ if (ucred)
+ kauth_cred_rele(ucred);
+ }
+}
+
+
+void vnode_cache_credentials(vnode_t vp, vfs_context_t context)
+{
+ kauth_cred_t ucred;
+ kauth_cred_t tcred = NOCRED;
+ struct timeval tv;
+
+ ucred = vfs_context_ucred(context);
+
+ if (vp->v_cred != ucred || (vp->v_mount->mnt_kern_flag & MNTK_AUTH_OPAQUE)) {
+ vnode_lock(vp);
+
+ microuptime(&tv);
+ vp->v_cred_timestamp = tv.tv_sec;
+
+ if (vp->v_cred != ucred) {
+ kauth_cred_ref(ucred);
+
+ tcred = vp->v_cred;
+ vp->v_cred = ucred;
+ }
+ vnode_unlock(vp);
+
+ if (tcred)
+ kauth_cred_rele(tcred);
+ }
+}
+
+/* reverse_lookup - lookup by walking back up the parent chain while leveraging
+ * use of the name cache lock in order to protect our starting vnode.
+ * NOTE - assumes you already have search access to starting point.
+ * returns 0 when we have reached the root, current working dir, or chroot root
+ *
+ */
+int
+reverse_lookup(vnode_t start_vp, vnode_t *lookup_vpp, struct filedesc *fdp, vfs_context_t context, int *dp_authorized)
+{
+ int vid, done = 0;
+ int auth_opaque = 0;
+ vnode_t dp = start_vp;
+ vnode_t vp = NULLVP;
+ kauth_cred_t ucred;
+ struct timeval tv;
+
+ ucred = vfs_context_ucred(context);
+ *lookup_vpp = start_vp;
+
+ name_cache_lock();
+
+ if ( dp->v_mount && (dp->v_mount->mnt_kern_flag & MNTK_AUTH_OPAQUE) ) {
+ auth_opaque = 1;
+ microuptime(&tv);
+ }
+ for (;;) {
+ *dp_authorized = 0;
+
+ if (auth_opaque && ((tv.tv_sec - dp->v_cred_timestamp) > VCRED_EXPIRED))
+ break;
+ if (dp->v_cred != ucred)
+ break;
+ /*
+ * indicate that we're allowed to traverse this directory...
+ * even if we bail for some reason, this information is valid and is used
+ * to avoid doing a vnode_authorize
+ */
+ *dp_authorized = 1;
+
+ if ((dp->v_flag & VROOT) != 0 || /* Hit "/" */
+ (dp == fdp->fd_cdir) || /* Hit process's working directory */
+ (dp == fdp->fd_rdir)) { /* Hit process chroot()-ed root */
+ done = 1;
+ break;
+ }
+
+ if ( (vp = dp->v_parent) == NULLVP)
+ break;
+
+ dp = vp;
+ *lookup_vpp = dp;
+ } /* for (;;) */
+
+ vid = dp->v_id;
+
+ name_cache_unlock();
+
+ if (done == 0 && dp != start_vp) {
+ if (vnode_getwithvid(dp, vid) != 0) {
+ *lookup_vpp = start_vp;
+ }
+ }
+
+ return((done == 1) ? 0 : -1);
+}
+
+int
+cache_lookup_path(struct nameidata *ndp, struct componentname *cnp, vnode_t dp, vfs_context_t context, int *trailing_slash, int *dp_authorized)
+{
+ char *cp; /* pointer into pathname argument */
+ int vid, vvid;
+ int auth_opaque = 0;
+ vnode_t vp = NULLVP;
+ vnode_t tdp = NULLVP;
+ kauth_cred_t ucred;
+ struct timeval tv;
+ unsigned int hash;
+
+ ucred = vfs_context_ucred(context);
+ *trailing_slash = 0;
+
+ name_cache_lock();
+
+
+ if ( dp->v_mount && (dp->v_mount->mnt_kern_flag & MNTK_AUTH_OPAQUE) ) {
+ auth_opaque = 1;
+ microuptime(&tv);
+ }
+ for (;;) {
+ /*
+ * Search a directory.
+ *
+ * The cn_hash value is for use by cache_lookup
+ * The last component of the filename is left accessible via
+ * cnp->cn_nameptr for callers that need the name.
+ */
+ hash = 0;
+ cp = cnp->cn_nameptr;
+
+ while (*cp && (*cp != '/')) {
+ hash ^= crc32tab[((hash >> 24) ^ (unsigned char)*cp++)];
+ }
+ /*
+ * the crc generator can legitimately generate
+ * a 0... however, 0 for us means that we
+ * haven't computed a hash, so use 1 instead
+ */
+ if (hash == 0)
+ hash = 1;
+ cnp->cn_hash = hash;
+ cnp->cn_namelen = cp - cnp->cn_nameptr;
+
+ ndp->ni_pathlen -= cnp->cn_namelen;
+ ndp->ni_next = cp;
+
+ /*
+ * Replace multiple slashes by a single slash and trailing slashes
+ * by a null. This must be done before VNOP_LOOKUP() because some
+ * fs's don't know about trailing slashes. Remember if there were
+ * trailing slashes to handle symlinks, existing non-directories
+ * and non-existing files that won't be directories specially later.
+ */
+ while (*cp == '/' && (cp[1] == '/' || cp[1] == '\0')) {
+ cp++;
+ ndp->ni_pathlen--;
+
+ if (*cp == '\0') {
+ *trailing_slash = 1;
+ *ndp->ni_next = '\0';
+ }
+ }
+ ndp->ni_next = cp;
+
+ cnp->cn_flags &= ~(MAKEENTRY | ISLASTCN | ISDOTDOT);
+
+ if (*cp == '\0')
+ cnp->cn_flags |= ISLASTCN;
+
+ if (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
+ cnp->cn_flags |= ISDOTDOT;
+
+ *dp_authorized = 0;
+
+ if (auth_opaque && ((tv.tv_sec - dp->v_cred_timestamp) > VCRED_EXPIRED))
+ break;
+
+ if (dp->v_cred != ucred)
+ break;
+ /*
+ * indicate that we're allowed to traverse this directory...
+ * even if we fail the cache lookup or decide to bail for
+ * some other reason, this information is valid and is used
+ * to avoid doing a vnode_authorize before the call to VNOP_LOOKUP
+ */
+ *dp_authorized = 1;
+
+ if ( (cnp->cn_flags & (ISLASTCN | ISDOTDOT)) ) {
+ if (cnp->cn_nameiop != LOOKUP)
+ break;
+ if (cnp->cn_flags & (LOCKPARENT | NOCACHE))
+ break;
+ if (cnp->cn_flags & ISDOTDOT) {
+ /*
+ * Quit here only if we can't use
+ * the parent directory pointer or
+ * don't have one. Otherwise, we'll
+ * use it below.
+ */
+ if ((dp->v_flag & VROOT) ||
+ dp->v_parent == NULLVP)
+ break;
+ }
+ }
+
+ /*
+ * "." and ".." aren't supposed to be cached, so check
+ * for them before checking the cache.
+ */
+ if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')
+ vp = dp;
+ else if (cnp->cn_flags & ISDOTDOT)
+ vp = dp->v_parent;
+ else {
+ if ( (vp = cache_lookup_locked(dp, cnp)) == NULLVP)
+ break;
+ }
+
+ if ( (cnp->cn_flags & ISLASTCN) )
+ break;
+
+ if (vp->v_type != VDIR) {
+ if (vp->v_type != VLNK)
+ vp = NULL;
+ break;
+ }
+ if (vp->v_mountedhere && ((cnp->cn_flags & NOCROSSMOUNT) == 0))
+ break;
+
+ dp = vp;
+ vp = NULLVP;
+
+ cnp->cn_nameptr = ndp->ni_next + 1;
+ ndp->ni_pathlen--;
+ while (*cnp->cn_nameptr == '/') {
+ cnp->cn_nameptr++;
+ ndp->ni_pathlen--;
+ }
+ }
+ if (vp != NULLVP)
+ vvid = vp->v_id;
+ vid = dp->v_id;
+
+ name_cache_unlock();
+
+
+ if ((vp != NULLVP) && (vp->v_type != VLNK) &&
+ ((cnp->cn_flags & (ISLASTCN | LOCKPARENT | WANTPARENT | SAVESTART)) == ISLASTCN)) {
+ /*
+ * if we've got a child and it's the last component, and
+ * the lookup doesn't need to return the parent then we
+ * can skip grabbing an iocount on the parent, since all
+ * we're going to do with it is a vnode_put just before
+ * we return from 'lookup'. If it's a symbolic link,
+ * we need the parent in case the link happens to be
+ * a relative pathname.
+ */
+ tdp = dp;
+ dp = NULLVP;
+ } else {
+need_dp:
+ /*
+ * return the last directory we looked at
+ * with an io reference held
+ */
+ if (dp == ndp->ni_usedvp) {
+ /*
+ * if this vnode matches the one passed in via USEDVP
+ * than this context already holds an io_count... just
+ * use vnode_get to get an extra ref for lookup to play
+ * with... can't use the getwithvid variant here because
+ * it will block behind a vnode_drain which would result
+ * in a deadlock (since we already own an io_count that the
+ * vnode_drain is waiting on)... vnode_get grabs the io_count
+ * immediately w/o waiting... it always succeeds
+ */
+ vnode_get(dp);
+ } else if ( (vnode_getwithvid(dp, vid)) ) {
+ /*
+ * failure indicates the vnode
+ * changed identity or is being
+ * TERMINATED... in either case
+ * punt this lookup
+ */
+ return (ENOENT);
+ }
+ }
+ if (vp != NULLVP) {
+ if ( (vnode_getwithvid(vp, vvid)) ) {
+ vp = NULLVP;
+
+ /*
+ * can't get reference on the vp we'd like
+ * to return... if we didn't grab a reference
+ * on the directory (due to fast path bypass),
+ * then we need to do it now... we can't return
+ * with both ni_dvp and ni_vp NULL, and no
+ * error condition
+ */
+ if (dp == NULLVP) {
+ dp = tdp;
+ goto need_dp;
+ }
+ }
+ }
+ ndp->ni_dvp = dp;
+ ndp->ni_vp = vp;
+
+ return (0);
+}
+
+
+static vnode_t
+cache_lookup_locked(vnode_t dvp, struct componentname *cnp)
+{
+ register struct namecache *ncp;
+ register struct nchashhead *ncpp;
+ register long namelen = cnp->cn_namelen;
+ char *nameptr = cnp->cn_nameptr;
+ unsigned int hashval = (cnp->cn_hash & NCHASHMASK);
+ vnode_t vp;
+
+ ncpp = NCHHASH(dvp, cnp->cn_hash);
+ LIST_FOREACH(ncp, ncpp, nc_hash) {
+ if ((ncp->nc_dvp == dvp) && (ncp->nc_hashval == hashval)) {
+ if (memcmp(ncp->nc_name, nameptr, namelen) == 0 && ncp->nc_name[namelen] == 0)
+ break;
+ }
+ }
+ if (ncp == 0)
+ /*
+ * We failed to find an entry
+ */
+ return (NULL);
+
+ vp = ncp->nc_vp;
+ if (vp && (vp->v_flag & VISHARDLINK)) {
+ /*
+ * The file system wants a VNOP_LOOKUP on this vnode
+ */
+ vp = NULL;
+ }
+
+ return (vp);
+}
+
+
+//
+// Have to take a len argument because we may only need to
+// hash part of a componentname.
+//
+static unsigned int
+hash_string(const char *cp, int len)
+{
+ unsigned hash = 0;
+
+ if (len) {
+ while (len--) {
+ hash ^= crc32tab[((hash >> 24) ^ (unsigned char)*cp++)];
+ }
+ } else {
+ while (*cp != '\0') {
+ hash ^= crc32tab[((hash >> 24) ^ (unsigned char)*cp++)];
+ }
+ }
+ /*
+ * the crc generator can legitimately generate
+ * a 0... however, 0 for us means that we
+ * haven't computed a hash, so use 1 instead
+ */
+ if (hash == 0)
+ hash = 1;
+ return hash;