Print this page
        
*** 19,29 ****
   * CDDL HEADER END
   */
  
  /*
   * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
!  * Copyright 2016 Joyent, Inc.
   * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
   */
  
  /*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T     */
  /*        All Rights Reserved   */
--- 19,29 ----
   * CDDL HEADER END
   */
  
  /*
   * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
!  * Copyright (c) 2015, Joyent, Inc. All rights reserved.
   * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
   */
  
  /*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T     */
  /*        All Rights Reserved   */
*** 977,1105 ****
  
          return (ret);
  }
  
  /*
-  * Clean a stale v_path from a vnode.  This is only performed if the v_path has
-  * not been altered since it was found to be stale
-  */
- static void
- vnode_clear_vpath(vnode_t *vp, char *vpath_old)
- {
-         mutex_enter(&vp->v_lock);
-         if (vp->v_path != vn_vpath_empty && vp->v_path == vpath_old) {
-                 vp->v_path = vn_vpath_empty;
-                 mutex_exit(&vp->v_lock);
-                 kmem_free(vpath_old, strlen(vpath_old) + 1);
-         } else {
-                 mutex_exit(&vp->v_lock);
-         }
- }
- 
- /*
-  * Validate that a pathname refers to a given vnode.
-  */
- static int
- vnode_valid_pn(vnode_t *vp, vnode_t *vrootp, pathname_t *pn, pathname_t *rpn,
-     int flags, cred_t *cr)
- {
-         vnode_t *compvp;
-         /*
-          * If we are in a zone or a chroot environment, then we have to
-          * take additional steps, since the path to the root might not
-          * be readable with the current credentials, even though the
-          * process can legitmately access the file.  In this case, we
-          * do the following:
-          *
-          * lookuppnvp() with all privileges to get the resolved path.
-          * call localpath() to get the local portion of the path, and
-          * continue as normal.
-          *
-          * If the the conversion to a local path fails, then we continue
-          * as normal.  This is a heuristic to make process object file
-          * paths available from within a zone.  Because lofs doesn't
-          * support page operations, the vnode stored in the seg_t is
-          * actually the underlying real vnode, not the lofs node itself.
-          * Most of the time, the lofs path is the same as the underlying
-          * vnode (for example, /usr/lib/libc.so.1).
-          */
-         if (vrootp != rootdir) {
-                 char *local = NULL;
- 
-                 VN_HOLD(rootdir);
-                 if (lookuppnvp(pn, rpn, FOLLOW, NULL, &compvp, rootdir,
-                     rootdir, kcred) == 0) {
-                         local = localpath(rpn->pn_path, vrootp, kcred);
-                         VN_RELE(compvp);
-                 }
- 
-                 /*
-                  * The original pn was changed through lookuppnvp().
-                  * Set it to local for next validation attempt.
-                  */
-                 if (local) {
-                         (void) pn_set(pn, local);
-                 } else {
-                         return (1);
-                 }
-         }
- 
-         /*
-          * We should have a local path at this point, so start the search from
-          * the root of the current process.
-          */
-         VN_HOLD(vrootp);
-         if (vrootp != rootdir)
-                 VN_HOLD(vrootp);
-         if (lookuppnvp(pn, rpn, FOLLOW | flags, NULL, &compvp, vrootp, vrootp,
-             cr) == 0) {
-                 /*
-                  * Check to see if the returned vnode is the same as the one we
-                  * expect.
-                  */
-                 if (vn_compare(vp, compvp) ||
-                     vnode_match(vp, compvp, cr)) {
-                         VN_RELE(compvp);
-                         return (0);
-                 } else {
-                         VN_RELE(compvp);
-                 }
-         }
- 
-         return (1);
- }
- 
- /*
-  * Struct for tracking vnodes with invalidated v_path entries during a
-  * dirtopath reverse lookup.  By keepeing adequate state, those vnode can be
-  * revisted to populate v_path.
-  */
- struct dirpath_walk {
-         struct dirpath_walk     *dw_next;
-         vnode_t                 *dw_vnode;
-         vnode_t                 *dw_pvnode;
-         size_t                  dw_len;
-         char                    *dw_name;
- };
- 
- /*
   * Given a directory, return the full, resolved path.  This looks up "..",
   * searches for the given vnode in the parent, appends the component, etc.  It
   * is used to implement vnodetopath() and getcwd() when the cached path fails.
   */
  static int
  dirtopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, int flags,
      cred_t *cr)
  {
          pathname_t      pn, rpn, emptypn;
!         vnode_t         *pvp = NULL, *startvp = vp;
!         int             err = 0;
          size_t          complen;
          dirent64_t      *dp;
!         char            *bufloc, *dbuf;
!         const size_t    dlen = DIRENT64_RECLEN(MAXPATHLEN);
!         struct dirpath_walk *dw_chain = NULL, *dw_entry;
  
          /* Operation only allowed on directories */
          ASSERT(vp->v_type == VDIR);
  
          /* We must have at least enough space for "/" */
--- 977,1004 ----
  
          return (ret);
  }
  
  /*
   * Given a directory, return the full, resolved path.  This looks up "..",
   * searches for the given vnode in the parent, appends the component, etc.  It
   * is used to implement vnodetopath() and getcwd() when the cached path fails.
   */
  static int
  dirtopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, int flags,
      cred_t *cr)
  {
          pathname_t pn, rpn, emptypn;
!         vnode_t *cmpvp, *pvp = NULL;
!         vnode_t *startvp = vp;
!         int err = 0, vprivs;
          size_t complen;
+         char *dbuf;
          dirent64_t *dp;
!         char            *bufloc;
!         size_t          dlen = DIRENT64_RECLEN(MAXPATHLEN);
!         refstr_t        *mntpt;
  
          /* Operation only allowed on directories */
          ASSERT(vp->v_type == VDIR);
  
          /* We must have at least enough space for "/" */
*** 1120,1132 ****
           * during the loop.
           */
          VN_HOLD(vp);
  
          for (;;) {
-                 int vprivs;
-                 hrtime_t cached_stamp;
- 
                  /*
                   * Return if we've reached the root.  If the buffer is empty,
                   * return '/'.  We explicitly don't use vn_compare(), since it
                   * compares the real vnodes.  A lofs mount of '/' would produce
                   * incorrect results otherwise.
--- 1019,1028 ----
*** 1147,1187 ****
                          err = EPERM;
                          goto out;
                  }
  
                  /*
                   * Shortcut: see if this vnode has correct v_path. If so,
                   * we have the work done.
                   */
                  mutex_enter(&vp->v_lock);
!                 if (vp->v_path != vn_vpath_empty &&
!                     pn_set(&pn, vp->v_path) == 0) {
!                         cached_stamp = vp->v_path_stamp;
                          mutex_exit(&vp->v_lock);
                          rpn.pn_path = rpn.pn_buf;
  
!                         /* Ensure the v_path pointing to correct vnode */
!                         if (vnode_valid_pn(vp, vrootp, &pn, &rpn, flags,
!                             cr) == 0) {
                                  complen = strlen(rpn.pn_path);
                                  bufloc -= complen;
                                  if (bufloc < buf) {
                                          err = ERANGE;
                                          goto out;
                                  }
!                                 bcopy(rpn.pn_path, bufloc, complen);
                                  break;
                          } else {
!                                 /*
!                                  * Immediately nuke cached v_path entries known
!                                  * to be invalid.
!                                  */
!                                 vn_clearpath(vp, cached_stamp);
                          }
                  } else {
                          mutex_exit(&vp->v_lock);
                  }
  
                  /*
                   * Shortcuts failed, search for this vnode in its parent.  If
                   * this is a mountpoint, then get the vnode underneath.
                   */
--- 1043,1135 ----
                          err = EPERM;
                          goto out;
                  }
  
                  /*
+                  * Shortcut: see if this vnode is a mountpoint.  If so,
+                  * grab the path information from the vfs_t.
+                  */
+                 if (vp->v_flag & VROOT) {
+ 
+                         mntpt = vfs_getmntpoint(vp->v_vfsp);
+                         if ((err = pn_set(&pn, (char *)refstr_value(mntpt)))
+                             == 0) {
+                                 refstr_rele(mntpt);
+                                 rpn.pn_path = rpn.pn_buf;
+ 
+                                 /*
+                                  * Ensure the mountpoint still exists.
+                                  */
+                                 VN_HOLD(vrootp);
+                                 if (vrootp != rootdir)
+                                         VN_HOLD(vrootp);
+                                 if (lookuppnvp(&pn, &rpn, flags, NULL,
+                                     &cmpvp, vrootp, vrootp, cr) == 0) {
+ 
+                                         if (VN_CMP(vp, cmpvp)) {
+                                                 VN_RELE(cmpvp);
+ 
+                                                 complen = strlen(rpn.pn_path);
+                                                 bufloc -= complen;
+                                                 if (bufloc < buf) {
+                                                         err = ERANGE;
+                                                         goto out;
+                                                 }
+                                                 bcopy(rpn.pn_path, bufloc,
+                                                     complen);
+                                                 break;
+                                         } else {
+                                                 VN_RELE(cmpvp);
+                                         }
+                                 }
+                         } else {
+                                 refstr_rele(mntpt);
+                         }
+                 }
+ 
+                 /*
                   * Shortcut: see if this vnode has correct v_path. If so,
                   * we have the work done.
                   */
                  mutex_enter(&vp->v_lock);
!                 if (vp->v_path != NULL) {
! 
!                         if ((err = pn_set(&pn, vp->v_path)) == 0) {
                                  mutex_exit(&vp->v_lock);
                                  rpn.pn_path = rpn.pn_buf;
  
!                                 /*
!                                  * Ensure the v_path pointing to correct vnode
!                                  */
!                                 VN_HOLD(vrootp);
!                                 if (vrootp != rootdir)
!                                         VN_HOLD(vrootp);
!                                 if (lookuppnvp(&pn, &rpn, flags, NULL,
!                                     &cmpvp, vrootp, vrootp, cr) == 0) {
! 
!                                         if (VN_CMP(vp, cmpvp)) {
!                                                 VN_RELE(cmpvp);
! 
                                                  complen = strlen(rpn.pn_path);
                                                  bufloc -= complen;
                                                  if (bufloc < buf) {
                                                          err = ERANGE;
                                                          goto out;
                                                  }
!                                                 bcopy(rpn.pn_path, bufloc,
!                                                     complen);
                                                  break;
                                          } else {
!                                                 VN_RELE(cmpvp);
                                          }
+                                 }
                          } else {
                                  mutex_exit(&vp->v_lock);
                          }
+                 } else {
+                         mutex_exit(&vp->v_lock);
+                 }
  
                  /*
                   * Shortcuts failed, search for this vnode in its parent.  If
                   * this is a mountpoint, then get the vnode underneath.
                   */
*** 1218,1227 ****
--- 1166,1207 ----
                  if ((err = VOP_ACCESS(pvp, vprivs, 0, cr, NULL)) != 0) {
                          goto out;
                  }
  
                  /*
+                  * Try to obtain the path component from dnlc cache
+                  * before searching through the directory.
+                  */
+                 if ((cmpvp = dnlc_reverse_lookup(vp, dbuf, dlen)) != NULL) {
+                         /*
+                          * If we got parent vnode as a result,
+                          * then the answered path is correct.
+                          */
+                         if (VN_CMP(cmpvp, pvp)) {
+                                 VN_RELE(cmpvp);
+                                 complen = strlen(dbuf);
+                                 bufloc -= complen;
+                                 if (bufloc <= buf) {
+                                         err = ENAMETOOLONG;
+                                         goto out;
+                                 }
+                                 bcopy(dbuf, bufloc, complen);
+ 
+                                 /* Prepend a slash to the current path */
+                                 *--bufloc = '/';
+ 
+                                 /* And continue with the next component */
+                                 VN_RELE(vp);
+                                 vp = pvp;
+                                 pvp = NULL;
+                                 continue;
+                         } else {
+                                 VN_RELE(cmpvp);
+                         }
+                 }
+ 
+                 /*
                   * Search the parent directory for the entry corresponding to
                   * this vnode.
                   */
                  if ((err = dirfindvp(vrootp, pvp, vp, cr, dbuf, dlen, &dp))
                      != 0)
*** 1235,1257 ****
                  bcopy(dp->d_name, bufloc, complen);
  
                  /* Prepend a slash to the current path.  */
                  *--bufloc = '/';
  
-                 /*
-                  * Record the name and directory for later reconstruction and
-                  * link it up with the others.
-                  */
-                 dw_entry = kmem_alloc(sizeof (*dw_entry), KM_SLEEP);
-                 dw_entry->dw_name = kmem_alloc(complen + 1, KM_SLEEP);
-                 VN_HOLD(dw_entry->dw_vnode = vp);
-                 VN_HOLD(dw_entry->dw_pvnode = pvp);
-                 bcopy(dp->d_name, dw_entry->dw_name, complen + 1);
-                 dw_entry->dw_len = complen;
-                 dw_entry->dw_next = dw_chain;
-                 dw_chain = dw_entry;
- 
                  /* And continue with the next component */
                  VN_RELE(vp);
                  vp = pvp;
                  pvp = NULL;
          }
--- 1215,1224 ----
*** 1262,1302 ****
          if (bufloc != buf)
                  ovbcopy(bufloc, buf, buflen - (bufloc - buf));
  
  out:
          /*
-          * Walk over encountered directory entries which were afflicted with a
-          * stale or absent v_path.  If the dirtopath was successful, we should
-          * possess the necessary information to populate all of them with a
-          * valid v_path.
-          *
-          * While processing this list, it is safe to call vn_setpath despite
-          * the fact that racing vnode actions may have altered v_path entries
-          * while the above loopwas still executing.  Any updated entries will
-          * have a newer v_path_stamp value which prevents an invalid overwrite.
-          *
-          * If an error was encountered during the search, freeing the chain is
-          * still required.
-          */
-         dw_entry = dw_chain;
-         while (dw_entry != NULL) {
-                 struct dirpath_walk *next = dw_entry->dw_next;
- 
-                 if (err == 0) {
-                         vn_setpath(NULL, dw_entry->dw_pvnode,
-                             dw_entry->dw_vnode, dw_entry->dw_name,
-                             dw_entry->dw_len);
-                 }
- 
-                 VN_RELE(dw_entry->dw_vnode);
-                 VN_RELE(dw_entry->dw_pvnode);
-                 kmem_free(dw_entry->dw_name, dw_entry->dw_len + 1);
-                 kmem_free(dw_entry, sizeof (*dw_entry));
-                 dw_entry = next;
-         }
- 
-         /*
           * If the error was ESTALE and the current directory to look in
           * was the root for this lookup, the root for a mounted file
           * system, or the starting directory for lookups, then
           * return ENOENT instead of ESTALE.  In this case, no recovery
           * is possible by the higher level.  If ESTALE was returned for
--- 1229,1238 ----
*** 1332,1353 ****
   */
  static int
  vnodetopath_common(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen,
      cred_t *cr, int flags)
  {
!         pathname_t pn;
!         int ret = 0;
!         vnode_t *realvp;
!         boolean_t doclose = B_FALSE;
  
          /*
           * If vrootp is NULL, get the root for curproc.  Callers with any other
           * requirements should pass in a different vrootp.
           */
          if (vrootp == NULL) {
-                 proc_t *p = curproc;
- 
                  mutex_enter(&p->p_lock);
                  if ((vrootp = PTOU(p)->u_rdir) == NULL)
                          vrootp = rootdir;
                  VN_HOLD(vrootp);
                  mutex_exit(&p->p_lock);
--- 1268,1289 ----
   */
  static int
  vnodetopath_common(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen,
      cred_t *cr, int flags)
  {
!         pathname_t pn, rpn;
!         int ret, len;
!         vnode_t *compvp, *pvp, *realvp;
!         proc_t *p = curproc;
!         char path[MAXNAMELEN];
!         int doclose = 0;
  
          /*
           * If vrootp is NULL, get the root for curproc.  Callers with any other
           * requirements should pass in a different vrootp.
           */
          if (vrootp == NULL) {
                  mutex_enter(&p->p_lock);
                  if ((vrootp = PTOU(p)->u_rdir) == NULL)
                          vrootp = rootdir;
                  VN_HOLD(vrootp);
                  mutex_exit(&p->p_lock);
*** 1365,1427 ****
           */
          if (vp->v_type == VDIR && VOP_REALVP(vp, &realvp, NULL) == 0 &&
              realvp != vp) {
                  VN_HOLD(vp);
                  if (VOP_OPEN(&vp, FREAD, cr, NULL) == 0)
!                         doclose = B_TRUE;
                  else
                          VN_RELE(vp);
          }
  
          /*
!          * Check to see if we have a valid cached path in the vnode.
           */
-         pn_alloc(&pn);
          mutex_enter(&vp->v_lock);
!         if (vp->v_path != vn_vpath_empty) {
!                 hrtime_t cached_stamp;
!                 pathname_t rpn;
! 
!                 cached_stamp = vp->v_path_stamp;
                  (void) pn_set(&pn, vp->v_path);
                  mutex_exit(&vp->v_lock);
  
                  /* We should only cache absolute paths */
                  ASSERT(pn.pn_buf[0] == '/');
  
!                 pn_alloc(&rpn);
!                 if (vnode_valid_pn(vp, vrootp, &pn, &rpn, flags, cr) == 0) {
!                         /* Return the result, if we're able. */
!                         if (buflen > rpn.pn_pathlen) {
!                                 bcopy(rpn.pn_path, buf, rpn.pn_pathlen + 1);
                          } else {
!                                 ret = ENAMETOOLONG;
                          }
                          pn_free(&pn);
                          pn_free(&rpn);
!                         goto out;
                  }
                  pn_free(&rpn);
-                 vn_clearpath(vp, cached_stamp);
          } else {
                  mutex_exit(&vp->v_lock);
          }
          pn_free(&pn);
  
!         if (vp->v_type != VDIR) {
                  /*
!                  * The reverse lookup tricks used by dirtopath aren't possible
!                  * for non-directory entries.  The best which can be done is
!                  * clearing any stale v_path so later lookups can potentially
!                  * repopulate it with a valid path.
                   */
                  ret = ENOENT;
          } else {
!                 ret = dirtopath(vrootp, vp, buf, buflen, flags, cr);
          }
  
! out:
          VN_RELE(vrootp);
          if (doclose) {
                  (void) VOP_CLOSE(vp, FREAD, 1, 0, cr, NULL);
                  VN_RELE(vp);
          }
--- 1301,1467 ----
           */
          if (vp->v_type == VDIR && VOP_REALVP(vp, &realvp, NULL) == 0 &&
              realvp != vp) {
                  VN_HOLD(vp);
                  if (VOP_OPEN(&vp, FREAD, cr, NULL) == 0)
!                         doclose = 1;
                  else
                          VN_RELE(vp);
          }
  
+         pn_alloc(&pn);
+ 
          /*
!          * Check to see if we have a cached path in the vnode.
           */
          mutex_enter(&vp->v_lock);
!         if (vp->v_path != NULL) {
                  (void) pn_set(&pn, vp->v_path);
                  mutex_exit(&vp->v_lock);
  
+                 pn_alloc(&rpn);
+ 
                  /* We should only cache absolute paths */
                  ASSERT(pn.pn_buf[0] == '/');
  
!                 /*
!                  * If we are in a zone or a chroot environment, then we have to
!                  * take additional steps, since the path to the root might not
!                  * be readable with the current credentials, even though the
!                  * process can legitmately access the file.  In this case, we
!                  * do the following:
!                  *
!                  * lookuppnvp() with all privileges to get the resolved path.
!                  * call localpath() to get the local portion of the path, and
!                  * continue as normal.
!                  *
!                  * If the the conversion to a local path fails, then we continue
!                  * as normal.  This is a heuristic to make process object file
!                  * paths available from within a zone.  Because lofs doesn't
!                  * support page operations, the vnode stored in the seg_t is
!                  * actually the underlying real vnode, not the lofs node itself.
!                  * Most of the time, the lofs path is the same as the underlying
!                  * vnode (for example, /usr/lib/libc.so.1).
!                  */
!                 if (vrootp != rootdir) {
!                         char *local = NULL;
!                         VN_HOLD(rootdir);
!                         if (lookuppnvp(&pn, &rpn, FOLLOW,
!                             NULL, &compvp, rootdir, rootdir, kcred) == 0) {
!                                 local = localpath(rpn.pn_path, vrootp,
!                                     kcred);
!                                 VN_RELE(compvp);
!                         }
! 
!                         /*
!                          * The original pn was changed through lookuppnvp().
!                          * Set it to local for next validation attempt.
!                          */
!                         if (local) {
!                                 (void) pn_set(&pn, local);
                          } else {
!                                 goto notcached;
                          }
+                 }
+ 
+                 /*
+                  * We should have a local path at this point, so start the
+                  * search from the root of the current process.
+                  */
+                 VN_HOLD(vrootp);
+                 if (vrootp != rootdir)
+                         VN_HOLD(vrootp);
+                 ret = lookuppnvp(&pn, &rpn, FOLLOW | flags, NULL,
+                     &compvp, vrootp, vrootp, cr);
+                 if (ret == 0) {
+                         /*
+                          * Check to see if the returned vnode is the same as
+                          * the one we expect.  If not, give up.
+                          */
+                         if (!vn_compare(vp, compvp) &&
+                             !vnode_match(vp, compvp, cr)) {
+                                 VN_RELE(compvp);
+                                 goto notcached;
+                         }
+ 
+                         VN_RELE(compvp);
+ 
+                         /*
+                          * Return the result.
+                          */
+                         if (buflen <= rpn.pn_pathlen)
+                                 goto notcached;
+ 
+                         bcopy(rpn.pn_path, buf, rpn.pn_pathlen + 1);
                          pn_free(&pn);
                          pn_free(&rpn);
!                         VN_RELE(vrootp);
!                         if (doclose) {
!                                 (void) VOP_CLOSE(vp, FREAD, 1, 0, cr, NULL);
!                                 VN_RELE(vp);
                          }
+                         return (0);
+                 }
+ 
+ notcached:
                  pn_free(&rpn);
          } else {
                  mutex_exit(&vp->v_lock);
          }
+ 
          pn_free(&pn);
  
!         if (PROC_IS_BRANDED(curproc)) {
                  /*
!                  * If v_path doesn't work out and we're in a branded zone,
!                  * we're not going to bother doing more work here:  because
!                  * directories from the global can be lofs mounted into odd
!                  * locations (e.g., /native in an lx zone), it is likely that
!                  * the DNLC reverse lookup will yield nothing.  Indeed, the
!                  * only certainty is that the DNLC reverse lookup will be
!                  * exceedingly painful; we save ourselves the substantial
!                  * grief of scanning the entire DNLC and kick out with ENOENT
!                  * in this case.
                   */
                  ret = ENOENT;
+         } else if (vp->v_type != VDIR) {
+                 /*
+                  * If we don't have a directory, try to find it in the dnlc via
+                  * reverse lookup.  Once this is found, we can use the regular
+                  * directory search to find the full path.
+                  */
+                 if ((pvp = dnlc_reverse_lookup(vp, path, MAXNAMELEN)) != NULL) {
+                         /*
+                          * Check if we have read privilege so, that
+                          * we can lookup the path in the directory
+                          */
+                         ret = 0;
+                         if ((flags & LOOKUP_CHECKREAD)) {
+                                 ret = VOP_ACCESS(pvp, VREAD, 0, cr, NULL);
+                         }
+                         if (ret == 0) {
+                                 ret = dirtopath(vrootp, pvp, buf, buflen,
+                                     flags, cr);
+                         }
+                         if (ret == 0) {
+                                 len = strlen(buf);
+                                 if (len + strlen(path) + 1 >= buflen) {
+                                         ret = ENAMETOOLONG;
                                  } else {
!                                         if (buf[len - 1] != '/')
!                                                 buf[len++] = '/';
!                                         bcopy(path, buf + len,
!                                             strlen(path) + 1);
                                  }
+                         }
  
!                         VN_RELE(pvp);
!                 } else
!                         ret = ENOENT;
!         } else
!                 ret = dirtopath(vrootp, vp, buf, buflen, flags, cr);
! 
          VN_RELE(vrootp);
          if (doclose) {
                  (void) VOP_CLOSE(vp, FREAD, 1, 0, cr, NULL);
                  VN_RELE(vp);
          }