[BACK]Return to vfs_lookup.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / kern

Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.

Diff for /src/sys/kern/vfs_lookup.c between version 1.212 and 1.212.4.9

version 1.212, 2019/07/18 09:39:40 version 1.212.4.9, 2020/01/25 15:54:03
Line 50  __KERNEL_RCSID(0, "$NetBSD$");
Line 50  __KERNEL_RCSID(0, "$NetBSD$");
 #include <sys/time.h>  #include <sys/time.h>
 #include <sys/namei.h>  #include <sys/namei.h>
 #include <sys/vnode.h>  #include <sys/vnode.h>
   #include <sys/vnode_impl.h>
 #include <sys/mount.h>  #include <sys/mount.h>
 #include <sys/errno.h>  #include <sys/errno.h>
 #include <sys/filedesc.h>  #include <sys/filedesc.h>
Line 535  namei_getstartdir(struct namei_state *st
Line 536  namei_getstartdir(struct namei_state *st
         struct nameidata *ndp = state->ndp;          struct nameidata *ndp = state->ndp;
         struct componentname *cnp = state->cnp;          struct componentname *cnp = state->cnp;
         struct cwdinfo *cwdi;           /* pointer to cwd state */          struct cwdinfo *cwdi;           /* pointer to cwd state */
         struct lwp *self = curlwp;      /* thread doing namei() */  
         struct vnode *rootdir, *erootdir, *curdir, *startdir;          struct vnode *rootdir, *erootdir, *curdir, *startdir;
   
         if (state->root_referenced) {          if (state->root_referenced) {
Line 546  namei_getstartdir(struct namei_state *st
Line 546  namei_getstartdir(struct namei_state *st
                 state->root_referenced = 0;                  state->root_referenced = 0;
         }          }
   
         cwdi = self->l_proc->p_cwdi;          /* NB: we must not block while holding the cwdi read locked. */
         rw_enter(&cwdi->cwdi_lock, RW_READER);          cwdi = cwdenter(RW_READER);
   
         /* root dir */          /* root dir */
         if (cwdi->cwdi_rdir == NULL || (cnp->cn_flags & NOCHROOT)) {          if (cwdi->cwdi_rdir == NULL || (cnp->cn_flags & NOCHROOT)) {
Line 605  namei_getstartdir(struct namei_state *st
Line 605  namei_getstartdir(struct namei_state *st
                 vref(state->ndp->ni_erootdir);                  vref(state->ndp->ni_erootdir);
         state->root_referenced = 1;          state->root_referenced = 1;
   
         rw_exit(&cwdi->cwdi_lock);          cwdexit(cwdi);
         return startdir;          return startdir;
 }  }
   
Line 710  namei_start(struct namei_state *state, i
Line 710  namei_start(struct namei_state *state, i
                 return ENOTDIR;                  return ENOTDIR;
         }          }
   
         vn_lock(startdir, LK_EXCLUSIVE | LK_RETRY);  
   
         *startdir_ret = startdir;          *startdir_ret = startdir;
         return 0;          return 0;
 }  }
Line 749  namei_follow(struct namei_state *state, 
Line 747  namei_follow(struct namei_state *state, 
         size_t linklen;          size_t linklen;
         int error;          int error;
   
         KASSERT(VOP_ISLOCKED(searchdir) == LK_EXCLUSIVE);  
         KASSERT(VOP_ISLOCKED(foundobj) == LK_EXCLUSIVE);  
         if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {          if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
                 return ELOOP;                  return ELOOP;
         }          }
   
           vn_lock(foundobj, LK_EXCLUSIVE | LK_RETRY);
         if (foundobj->v_mount->mnt_flag & MNT_SYMPERM) {          if (foundobj->v_mount->mnt_flag & MNT_SYMPERM) {
                 error = VOP_ACCESS(foundobj, VEXEC, cnp->cn_cred);                  error = VOP_ACCESS(foundobj, VEXEC, cnp->cn_cred);
                 if (error != 0)                  if (error != 0) {
                           VOP_UNLOCK(foundobj);
                         return error;                          return error;
                   }
         }          }
   
         /* FUTURE: fix this to not use a second buffer */          /* FUTURE: fix this to not use a second buffer */
Line 771  namei_follow(struct namei_state *state, 
Line 771  namei_follow(struct namei_state *state, 
         auio.uio_resid = MAXPATHLEN;          auio.uio_resid = MAXPATHLEN;
         UIO_SETUP_SYSSPACE(&auio);          UIO_SETUP_SYSSPACE(&auio);
         error = VOP_READLINK(foundobj, &auio, cnp->cn_cred);          error = VOP_READLINK(foundobj, &auio, cnp->cn_cred);
           VOP_UNLOCK(foundobj);
         if (error) {          if (error) {
                 PNBUF_PUT(cp);                  PNBUF_PUT(cp);
                 return error;                  return error;
Line 807  namei_follow(struct namei_state *state, 
Line 808  namei_follow(struct namei_state *state, 
         /* we're now starting from the beginning of the buffer again */          /* we're now starting from the beginning of the buffer again */
         cnp->cn_nameptr = ndp->ni_pnbuf;          cnp->cn_nameptr = ndp->ni_pnbuf;
   
         /* must unlock this before relocking searchdir */  
         VOP_UNLOCK(foundobj);  
   
         /*          /*
          * Check if root directory should replace current directory.           * Check if root directory should replace current directory.
          */           */
         if (ndp->ni_pnbuf[0] == '/') {          if (ndp->ni_pnbuf[0] == '/') {
                 vput(searchdir);                  vrele(searchdir);
                 /* Keep absolute symbolic links inside emulation root */                  /* Keep absolute symbolic links inside emulation root */
                 searchdir = ndp->ni_erootdir;                  searchdir = ndp->ni_erootdir;
                 if (searchdir == NULL ||                  if (searchdir == NULL ||
Line 825  namei_follow(struct namei_state *state, 
Line 823  namei_follow(struct namei_state *state, 
                         searchdir = ndp->ni_rootdir;                          searchdir = ndp->ni_rootdir;
                 }                  }
                 vref(searchdir);                  vref(searchdir);
                 vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);  
                 while (cnp->cn_nameptr[0] == '/') {                  while (cnp->cn_nameptr[0] == '/') {
                         cnp->cn_nameptr++;                          cnp->cn_nameptr++;
                         ndp->ni_pathlen--;                          ndp->ni_pathlen--;
Line 833  namei_follow(struct namei_state *state, 
Line 830  namei_follow(struct namei_state *state, 
         }          }
   
         *newsearchdir_ret = searchdir;          *newsearchdir_ret = searchdir;
         KASSERT(VOP_ISLOCKED(searchdir) == LK_EXCLUSIVE);  
         return 0;          return 0;
 }  }
   
Line 861  lookup_parsepath(struct namei_state *sta
Line 857  lookup_parsepath(struct namei_state *sta
          * responsibility for freeing the pathname buffer.           * responsibility for freeing the pathname buffer.
          *           *
          * At this point, our only vnode state is that the search dir           * At this point, our only vnode state is that the search dir
          * is held and locked.           * is held.
          */           */
         cnp->cn_consume = 0;          cnp->cn_consume = 0;
         cnp->cn_namelen = namei_getcomponent(cnp->cn_nameptr);          cnp->cn_namelen = namei_getcomponent(cnp->cn_nameptr);
Line 918  lookup_parsepath(struct namei_state *sta
Line 914  lookup_parsepath(struct namei_state *sta
 }  }
   
 /*  /*
    * Take care of crossing a mounted-on vnode.  On error, foundobj_ret will be
    * vrele'd, but searchdir is left alone.
    */
   static int
   lookup_crossmount(struct namei_state *state,
                     struct vnode **searchdir_ret,
                     struct vnode **foundobj_ret,
                     bool *searchdir_locked)
   {
           struct componentname *cnp = state->cnp;
           struct vnode *foundobj;
           struct vnode *searchdir;
           struct mount *mp;
           int error, lktype;
   
           searchdir = *searchdir_ret;
           foundobj = *foundobj_ret;
           error = 0;
   
           KASSERT((cnp->cn_flags & NOCROSSMOUNT) == 0);
           KASSERT(searchdir != NULL);
   
           /* First, unlock searchdir (oof). */
           if (*searchdir_locked) {
                   lktype = VOP_ISLOCKED(searchdir);
                   VOP_UNLOCK(searchdir);
                   *searchdir_locked = false;
           } else {
                   lktype = LK_NONE;
           }
   
           /*
            * Do an unlocked check to see if the vnode has been mounted on; if
            * so find the root of the mounted file system.
            */
           while (foundobj->v_type == VDIR &&
               (mp = foundobj->v_mountedhere) != NULL &&
               (cnp->cn_flags & NOCROSSMOUNT) == 0) {
                   KASSERTMSG(searchdir != foundobj, "same vn %p", searchdir);
                   /*
                    * First get the vnode stable.  LK_SHARED works brilliantly
                    * here because almost nothing else wants to lock the
                    * covered vnode.
                    */
                   error = vn_lock(foundobj, LK_SHARED);
                   if (error != 0) {
                           vrele(foundobj);
                           *foundobj_ret = NULL;
                           break;
                   }
   
                   /* Then check to see if something is still mounted on it. */
                   if ((mp = foundobj->v_mountedhere) == NULL) {
                           VOP_UNLOCK(foundobj);
                           break;
                   }
   
                   /* Get a reference to the mountpoint, and ditch foundobj. */
                   error = vfs_busy(mp);
                   vput(foundobj);
                   if (error != 0) {
                           *foundobj_ret = NULL;
                           break;
                   }
   
                   /* Now get a reference on the root vnode, and drop mount. */
                   error = VFS_ROOT(mp, LK_NONE, &foundobj);
                   vfs_unbusy(mp);
                   if (error) {
                           *foundobj_ret = NULL;
                           break;
                   }
   
                   /*
                    * Avoid locking vnodes from two filesystems because
                    * it's prone to deadlock, e.g. when using puffs.
                    * Also, it isn't a good idea to propagate slowness of
                    * a filesystem up to the root directory. For now,
                    * only handle the common case, where foundobj is
                    * VDIR.
                    *
                    * In this case set searchdir to null to avoid using
                    * it again. It is not correct to set searchdir ==
                    * foundobj here as that will confuse the caller.
                    * (See PR 40740.)
                    */
                   if (searchdir == NULL) {
                           /* already been here once; do nothing further */
                   } else if (foundobj->v_type == VDIR) {
                           vrele(searchdir);
                           *searchdir_ret = searchdir = NULL;
                           *foundobj_ret = foundobj;
                           lktype = LK_NONE;
                   }
           }
   
           /* If searchdir is still around, re-lock it. */
           if (error == 0 && lktype != LK_NONE) {
                   vn_lock(searchdir, lktype | LK_RETRY);
                   *searchdir_locked = true;
           }
           return error;
   }
   
   /*
  * Call VOP_LOOKUP for a single lookup; return a new search directory   * Call VOP_LOOKUP for a single lookup; return a new search directory
  * (used when crossing mountpoints up or searching union mounts down) and   * (used when crossing mountpoints up or searching union mounts down) and
  * the found object, which for create operations may be NULL on success.   * the found object, which for create operations may be NULL on success.
Line 933  static int
Line 1034  static int
 lookup_once(struct namei_state *state,  lookup_once(struct namei_state *state,
             struct vnode *searchdir,              struct vnode *searchdir,
             struct vnode **newsearchdir_ret,              struct vnode **newsearchdir_ret,
             struct vnode **foundobj_ret)              struct vnode **foundobj_ret,
               bool *newsearchdir_locked_ret)
 {  {
         struct vnode *tmpvn;            /* scratch vnode */          struct vnode *tmpvn;            /* scratch vnode */
         struct vnode *foundobj;         /* result */          struct vnode *foundobj;         /* result */
         struct mount *mp;               /* mount table entry */  
         struct lwp *l = curlwp;          struct lwp *l = curlwp;
         int error;          bool searchdir_locked = false;
           int error, lktype;
   
         struct componentname *cnp = state->cnp;          struct componentname *cnp = state->cnp;
         struct nameidata *ndp = state->ndp;          struct nameidata *ndp = state->ndp;
   
         KASSERT(cnp == &ndp->ni_cnd);          KASSERT(cnp == &ndp->ni_cnd);
         KASSERT(VOP_ISLOCKED(searchdir) == LK_EXCLUSIVE);  
         *newsearchdir_ret = searchdir;          *newsearchdir_ret = searchdir;
   
         /*          /*
Line 977  lookup_once(struct namei_state *state,
Line 1078  lookup_once(struct namei_state *state,
                         if (ndp->ni_rootdir != rootvnode) {                          if (ndp->ni_rootdir != rootvnode) {
                                 int retval;                                  int retval;
   
                                 VOP_UNLOCK(searchdir);  
                                 retval = vn_isunder(searchdir, ndp->ni_rootdir, l);                                  retval = vn_isunder(searchdir, ndp->ni_rootdir, l);
                                 vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);  
                                 if (!retval) {                                  if (!retval) {
                                     /* Oops! We got out of jail! */                                      /* Oops! We got out of jail! */
                                     log(LOG_WARNING,                                      log(LOG_WARNING,
Line 988  lookup_once(struct namei_state *state,
Line 1087  lookup_once(struct namei_state *state,
                                         p->p_pid, kauth_cred_geteuid(l->l_cred),                                          p->p_pid, kauth_cred_geteuid(l->l_cred),
                                         p->p_comm);                                          p->p_comm);
                                     /* Put us at the jail root. */                                      /* Put us at the jail root. */
                                     vput(searchdir);                                      vrele(searchdir);
                                     searchdir = NULL;                                      searchdir = NULL;
                                     foundobj = ndp->ni_rootdir;                                      foundobj = ndp->ni_rootdir;
                                     vref(foundobj);                                      vref(foundobj);
                                     vref(foundobj);                                      vref(foundobj);
                                     vn_lock(foundobj, LK_EXCLUSIVE | LK_RETRY);  
                                     *newsearchdir_ret = foundobj;                                      *newsearchdir_ret = foundobj;
                                     *foundobj_ret = foundobj;                                      *foundobj_ret = foundobj;
                                     error = 0;                                      error = 0;
Line 1006  lookup_once(struct namei_state *state,
Line 1104  lookup_once(struct namei_state *state,
                         tmpvn = searchdir;                          tmpvn = searchdir;
                         searchdir = searchdir->v_mount->mnt_vnodecovered;                          searchdir = searchdir->v_mount->mnt_vnodecovered;
                         vref(searchdir);                          vref(searchdir);
                         vput(tmpvn);                          vrele(tmpvn);
                         vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);  
                         *newsearchdir_ret = searchdir;                          *newsearchdir_ret = searchdir;
                 }                  }
         }          }
   
         /*          /*
            * If the file system supports VOP_LOOKUP() with a shared lock, and
            * we are not making any modifications (nameiop LOOKUP) or this is
            * not the last component then get a shared lock.  Where we can't do
            * fast-forwarded lookups (for example with layered file systems)
            * then this is the fallback for reducing lock contention.
            */
           if ((searchdir->v_mount->mnt_iflag & IMNT_SHRLOOKUP) != 0 &&
               (cnp->cn_nameiop == LOOKUP || (cnp->cn_flags & ISLASTCN) == 0)) {
                   lktype = LK_SHARED;
           } else {
                   lktype = LK_EXCLUSIVE;
           }
   
           /*
          * We now have a segment name to search for, and a directory to search.           * We now have a segment name to search for, and a directory to search.
          * Our vnode state here is that "searchdir" is held and locked.           * Our vnode state here is that "searchdir" is held.
          */           */
 unionlookup:  unionlookup:
         foundobj = NULL;          foundobj = NULL;
           if (!searchdir_locked) {
                   vn_lock(searchdir, lktype | LK_RETRY);
                   searchdir_locked = true;
           }
         error = VOP_LOOKUP(searchdir, &foundobj, cnp);          error = VOP_LOOKUP(searchdir, &foundobj, cnp);
   
         if (error != 0) {          if (error != 0) {
Line 1027  unionlookup:
Line 1142  unionlookup:
 #ifdef NAMEI_DIAGNOSTIC  #ifdef NAMEI_DIAGNOSTIC
                 printf("not found\n");                  printf("not found\n");
 #endif /* NAMEI_DIAGNOSTIC */  #endif /* NAMEI_DIAGNOSTIC */
   
                   /*
                    * If ENOLCK, the file system needs us to retry the lookup
                    * with an exclusive lock.  It's likely nothing was found in
                    * cache and/or modifications need to be made.
                    */
                   if (error == ENOLCK) {
                           KASSERT(VOP_ISLOCKED(searchdir) == LK_SHARED);
                           KASSERT(searchdir_locked);
                           if (vn_lock(searchdir, LK_UPGRADE | LK_NOWAIT)) {
                                   VOP_UNLOCK(searchdir);
                                   searchdir_locked = false;
                           }
                           lktype = LK_EXCLUSIVE;
                           goto unionlookup;
                   }
   
                 if ((error == ENOENT) &&                  if ((error == ENOENT) &&
                     (searchdir->v_vflag & VV_ROOT) &&                      (searchdir->v_vflag & VV_ROOT) &&
                     (searchdir->v_mount->mnt_flag & MNT_UNION)) {                      (searchdir->v_mount->mnt_flag & MNT_UNION)) {
Line 1034  unionlookup:
Line 1166  unionlookup:
                         searchdir = searchdir->v_mount->mnt_vnodecovered;                          searchdir = searchdir->v_mount->mnt_vnodecovered;
                         vref(searchdir);                          vref(searchdir);
                         vput(tmpvn);                          vput(tmpvn);
                         vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);                          searchdir_locked = false;
                         *newsearchdir_ret = searchdir;                          *newsearchdir_ret = searchdir;
                         goto unionlookup;                          goto unionlookup;
                 }                  }
Line 1088  unionlookup:
Line 1220  unionlookup:
                         cnp->cn_flags |= ISLASTCN;                          cnp->cn_flags |= ISLASTCN;
         }          }
   
         /*          /* Unlock, unless the caller needs the parent locked. */
          * "searchdir" is locked and held, "foundobj" is held,          if (searchdir != NULL) {
          * they may be the same vnode.                  KASSERT(searchdir_locked);
          */                  if ((cnp->cn_flags & (ISLASTCN | LOCKPARENT)) !=
         if (searchdir != foundobj) {                      (ISLASTCN | LOCKPARENT)) {
                 if (cnp->cn_flags & ISDOTDOT)                          VOP_UNLOCK(searchdir);
                         VOP_UNLOCK(searchdir);                          searchdir_locked = false;
                 error = vn_lock(foundobj, LK_EXCLUSIVE);  
                 if (cnp->cn_flags & ISDOTDOT)  
                         vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);  
                 if (error != 0) {  
                         vrele(foundobj);  
                         goto done;  
                 }                  }
           } else {
                   KASSERT(!searchdir_locked);
         }          }
   
         /*          *foundobj_ret = foundobj;
          * Check to see if the vnode has been mounted on;          error = 0;
          * if so find the root of the mounted file system.  done:
          */          *newsearchdir_locked_ret = searchdir_locked;
         KASSERT(searchdir != NULL);          return error;
         while (foundobj->v_type == VDIR &&  }
                (mp = foundobj->v_mountedhere) != NULL &&  
                (cnp->cn_flags & NOCROSSMOUNT) == 0) {  
   
                 KASSERT(searchdir != foundobj);  /*
    * Parse out the first path name component that we need to to consider.
    *
    * While doing this, attempt to use the name cache to fast-forward through
    * as many "easy" to find components of the path as possible.
    *
    * We use the namecache's node locks to form a chain, and avoid as many
    * vnode references and locks as possible.  In the ideal case, only the
    * final vnode will have its reference count adjusted and lock taken.
    */
   static int
   lookup_fastforward(struct namei_state *state, struct vnode **searchdir_ret,
                      struct vnode **foundobj_ret)
   {
           struct componentname *cnp = state->cnp;
           struct nameidata *ndp = state->ndp;
           krwlock_t *plock;
           struct vnode *foundobj, *searchdir;
           int error, error2;
           size_t oldpathlen;
           const char *oldnameptr;
   
                 error = vfs_busy(mp);          /*
                 if (error != 0) {           * Eat as many path name components as possible before giving up and
                         vput(foundobj);           * letting lookup_once() handle it.  Remember the starting point in
                         goto done;           * case we can't get vnode references and need to roll back.
            */
           plock = NULL;
           searchdir = *searchdir_ret;
           oldnameptr = cnp->cn_nameptr;
           oldpathlen = ndp->ni_pathlen;
           for (;;) {
                   foundobj = NULL;
   
                   /*
                    * Get the next component name.  There should be no slashes
                    * here, and we shouldn't have looped around if we were
                    * done.
                    */
                   KASSERT(cnp->cn_nameptr[0] != '/');
                   KASSERT(cnp->cn_nameptr[0] != '\0');
                   if ((error = lookup_parsepath(state)) != 0) {
                           break;
                 }                  }
                 if (searchdir != NULL) {  
                         VOP_UNLOCK(searchdir);                  /*
                    * Can't deal with dotdot lookups, because it means lock
                    * order reversal, and there are checks in lookup_once()
                    * that need to be made.  Also check for missing mountpoints.
                    */
                   if ((cnp->cn_flags & ISDOTDOT) != 0 ||
                       searchdir->v_mount == NULL) {
                           error = EOPNOTSUPP;
                           break;
                 }                  }
                 vput(foundobj);  
                 error = VFS_ROOT(mp, &foundobj);                  /*
                 vfs_unbusy(mp);                   * Can't deal with last component when modifying; this needs
                 if (error) {                   * searchdir locked and VOP_LOOKUP() called (which can and
                         if (searchdir != NULL) {                   * does modify state, despite the name).
                                 vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);                   */
                   if ((cnp->cn_flags & ISLASTCN) != 0) {
                           if (cnp->cn_nameiop != LOOKUP ||
                               (cnp->cn_flags & LOCKPARENT) != 0) {
                                   error = EOPNOTSUPP;
                                   break;
                         }                          }
                         goto done;  
                 }                  }
   
                 /*                  /*
                  * Avoid locking vnodes from two filesystems because                   * Good, now look for it in cache.  cache_lookup_linked()
                  * it's prone to deadlock, e.g. when using puffs.                   * will fail if there's nothing there, or if there's no
                  * Also, it isn't a good idea to propagate slowness of                   * ownership info for the directory, or if the user doesn't
                  * a filesystem up to the root directory. For now,                   * have permission to look up files in this directory.
                  * only handle the common case, where foundobj is                   */
                  * VDIR.                  if (!cache_lookup_linked(searchdir, cnp->cn_nameptr,
                       cnp->cn_namelen, &foundobj, &plock, cnp->cn_cred)) {
                           error = EOPNOTSUPP;
                           break;
                   }
                   KASSERT(plock != NULL && rw_lock_held(plock));
   
                   /* Scored a hit.  Negative is good too (ENOENT). */
                   if (foundobj == NULL) {
                           /* XXXAD need to handle -o union mount. */
                           error = ENOENT;
                           break;
                   }
   
                   /*
                    * Stop and get a hold on the vnode if there's something
                    * that can't be handled here:
                  *                   *
                  * In this case set searchdir to null to avoid using                   * - we've reached the last component.
                  * it again. It is not correct to set searchdir ==                   * - or encountered a mount point that needs to be crossed.
                  * foundobj here as that will confuse the caller.                   * - or encountered something other than a directory.
                  * (See PR 40740.)  
                  */                   */
                 if (searchdir == NULL) {                  if ((cnp->cn_flags & ISLASTCN) != 0 ||
                         /* already been here once; do nothing further */                      foundobj->v_type != VDIR ||
                 } else if (foundobj->v_type == VDIR) {                      (foundobj->v_type == VDIR &&
                         vrele(searchdir);                      foundobj->v_mountedhere != NULL)) {
                         *newsearchdir_ret = searchdir = NULL;                          mutex_enter(foundobj->v_interlock);
                           error = vcache_tryvget(foundobj);
                           /* v_interlock now released */
                           if (error != 0) {
                                   foundobj = NULL;
                           }
                           break;
                   }
   
                   /*
                    * Otherwise, we're still in business.  Set the found VDIR
                    * vnode as the search dir for the next component and
                    * continue on to it.
                    */
                   cnp->cn_nameptr = ndp->ni_next;
                   searchdir = foundobj;
           }
   
           /*
            * If we ended up with a new search dir, ref it before dropping the
            * namecache's lock.  The lock prevents both searchdir and foundobj
            * from disappearing.  If we can't ref the new searchdir, we have a
            * bit of a problem.  Roll back the fastforward to the beginning and
            * let lookup_once() take care of it.
            */
           if (searchdir != *searchdir_ret) {
                   mutex_enter(searchdir->v_interlock);
                   error2 = vcache_tryvget(searchdir);
                   /* v_interlock now unheld */
                   KASSERT(plock != NULL);
                   rw_exit(plock);
                   if (__predict_true(error2 == 0)) {
                           /* Returning new searchdir, and maybe new foundobj. */
                           vrele(*searchdir_ret);
                           *searchdir_ret = searchdir;
                 } else {                  } else {
                         VOP_UNLOCK(foundobj);                          /* Returning nothing. */
                         vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);                          if (foundobj != NULL) {
                         vn_lock(foundobj, LK_EXCLUSIVE | LK_RETRY);                                  vrele(foundobj);
                                   foundobj = NULL;
                           }
                           cnp->cn_nameptr = oldnameptr;
                           ndp->ni_pathlen = oldpathlen;
                           error = lookup_parsepath(state);
                 }                  }
           } else if (plock != NULL) {
                   /* Drop any namecache lock still held. */
                   rw_exit(plock);
         }          }
   
           KASSERT(error == 0 ? foundobj != NULL : foundobj == NULL);
         *foundobj_ret = foundobj;          *foundobj_ret = foundobj;
         error = 0;  
 done:  
         KASSERT(*newsearchdir_ret == NULL ||  
                 VOP_ISLOCKED(*newsearchdir_ret) == LK_EXCLUSIVE);  
         /*  
          * *foundobj_ret is valid only if error == 0.  
          */  
         KASSERT(error != 0 || *foundobj_ret == NULL ||  
             VOP_ISLOCKED(*foundobj_ret) == LK_EXCLUSIVE);  
         return error;          return error;
 }  }
   
Line 1183  namei_oneroot(struct namei_state *state,
Line 1408  namei_oneroot(struct namei_state *state,
         struct nameidata *ndp = state->ndp;          struct nameidata *ndp = state->ndp;
         struct componentname *cnp = state->cnp;          struct componentname *cnp = state->cnp;
         struct vnode *searchdir, *foundobj;          struct vnode *searchdir, *foundobj;
           bool searchdir_locked = false;
         int error;          int error;
   
         error = namei_start(state, isnfsd, &searchdir);          error = namei_start(state, isnfsd, &searchdir);
Line 1223  namei_oneroot(struct namei_state *state,
Line 1449  namei_oneroot(struct namei_state *state,
   
         for (;;) {          for (;;) {
                 KASSERT(searchdir != NULL);                  KASSERT(searchdir != NULL);
                 KASSERT(VOP_ISLOCKED(searchdir) == LK_EXCLUSIVE);                  KASSERT(!searchdir_locked);
   
                 /*                  /*
                  * If the directory we're on is unmounted, bail out.                   * Parse out the first path name component that we need to
                  * XXX: should this also check if it's unlinked?                   * to consider.  While doing this, attempt to use the name
                  * XXX: yes it should... but how?                   * cache to fast-forward through as many "easy" to find
                    * components of the path as possible.
                  */                   */
                 if (searchdir->v_mount == NULL) {                  error = lookup_fastforward(state, &searchdir, &foundobj);
                         vput(searchdir);  
                         ndp->ni_dvp = NULL;  
                         ndp->ni_vp = NULL;  
                         return (ENOENT);  
                 }  
   
                 /*                  /*
                  * Look up the next path component.                   * If we didn't get a good answer from the namecache, then
                  * (currently, this may consume more than one)                   * go directly to the file system.
                  */                   */
                   if (error != 0 && error != ENOENT) {
                           error = lookup_once(state, searchdir, &searchdir,
                               &foundobj, &searchdir_locked);
                   }
   
                 /* There should be no slashes here. */                  /*
                 KASSERT(cnp->cn_nameptr[0] != '/');                   * If the vnode we found is mounted on, then cross the mount
                    * and get the root vnode in foundobj.  If this encounters
                 /* and we shouldn't have looped around if we were done */                   * an error, it will dispose of foundobj, but searchdir is
                 KASSERT(cnp->cn_nameptr[0] != '\0');                   * untouched.
                    */
                 error = lookup_parsepath(state);                  if (error == 0 && foundobj != NULL &&
                 if (error) {                      foundobj->v_type == VDIR &&
                         vput(searchdir);                      foundobj->v_mountedhere != NULL &&
                         ndp->ni_dvp = NULL;                      (cnp->cn_flags & NOCROSSMOUNT) == 0) {
                         ndp->ni_vp = NULL;                          error = lookup_crossmount(state, &searchdir,
                         state->attempt_retry = 1;                              &foundobj, &searchdir_locked);
                         return (error);  
                 }                  }
   
                 error = lookup_once(state, searchdir, &searchdir, &foundobj);  
                 if (error) {                  if (error) {
                         if (searchdir != NULL) {                          if (searchdir != NULL) {
                                 vput(searchdir);                                  if (searchdir_locked) {
                                           searchdir_locked = false;
                                           vput(searchdir);
                                   } else {
                                           vrele(searchdir);
                                   }
                         }                          }
                         ndp->ni_dvp = NULL;                          ndp->ni_dvp = NULL;
                         ndp->ni_vp = NULL;                          ndp->ni_vp = NULL;
Line 1297  namei_oneroot(struct namei_state *state,
Line 1526  namei_oneroot(struct namei_state *state,
                  * them again.                   * them again.
                  */                   */
                 if (namei_atsymlink(state, foundobj)) {                  if (namei_atsymlink(state, foundobj)) {
                           /* Don't need searchdir locked any more. */
                           if (searchdir_locked) {
                                   searchdir_locked = false;
                                   VOP_UNLOCK(searchdir);
                           }
                         ndp->ni_pathlen += state->slashes;                          ndp->ni_pathlen += state->slashes;
                         ndp->ni_next -= state->slashes;                          ndp->ni_next -= state->slashes;
                         if (neverfollow) {                          if (neverfollow) {
Line 1338  namei_oneroot(struct namei_state *state,
Line 1572  namei_oneroot(struct namei_state *state,
                         if (error) {                          if (error) {
                                 KASSERT(searchdir != foundobj);                                  KASSERT(searchdir != foundobj);
                                 if (searchdir != NULL) {                                  if (searchdir != NULL) {
                                         vput(searchdir);                                          vrele(searchdir);
                                 }                                  }
                                 vput(foundobj);                                  vrele(foundobj);
                                 ndp->ni_dvp = NULL;                                  ndp->ni_dvp = NULL;
                                 ndp->ni_vp = NULL;                                  ndp->ni_vp = NULL;
                                 return error;                                  return error;
                         }                          }
                         /* namei_follow unlocks it (ugh) so rele, not put */  
                         vrele(foundobj);                          vrele(foundobj);
                         foundobj = NULL;                          foundobj = NULL;
   
Line 1376  namei_oneroot(struct namei_state *state,
Line 1609  namei_oneroot(struct namei_state *state,
                     (cnp->cn_flags & REQUIREDIR)) {                      (cnp->cn_flags & REQUIREDIR)) {
                         KASSERT(foundobj != searchdir);                          KASSERT(foundobj != searchdir);
                         if (searchdir) {                          if (searchdir) {
                                 vput(searchdir);                                  if (searchdir_locked) {
                                           searchdir_locked = false;
                                           vput(searchdir);
                                   } else {
                                           vrele(searchdir);
                                   }
                           } else {
                                   KASSERT(!searchdir_locked);
                         }                          }
                         vput(foundobj);                          vrele(foundobj);
                         ndp->ni_dvp = NULL;                          ndp->ni_dvp = NULL;
                         ndp->ni_vp = NULL;                          ndp->ni_vp = NULL;
                         state->attempt_retry = 1;                          state->attempt_retry = 1;
Line 1396  namei_oneroot(struct namei_state *state,
Line 1636  namei_oneroot(struct namei_state *state,
                  * Continue with the next component.                   * Continue with the next component.
                  */                   */
                 cnp->cn_nameptr = ndp->ni_next;                  cnp->cn_nameptr = ndp->ni_next;
                 if (searchdir == foundobj) {                  if (searchdir != NULL) {
                         vrele(searchdir);                          if (searchdir_locked) {
                 } else if (searchdir != NULL) {                                  searchdir_locked = false;
                         vput(searchdir);                                  vput(searchdir);
                           } else {
                                   vrele(searchdir);
                           }
                 }                  }
                 searchdir = foundobj;                  searchdir = foundobj;
                 foundobj = NULL;                  foundobj = NULL;
         }          }
   
           KASSERT((cnp->cn_flags & LOCKPARENT) == 0 || searchdir == NULL ||
               VOP_ISLOCKED(searchdir) == LK_EXCLUSIVE);
   
  skiploop:   skiploop:
   
         if (foundobj != NULL) {          if (foundobj != NULL) {
Line 1417  namei_oneroot(struct namei_state *state,
Line 1663  namei_oneroot(struct namei_state *state,
                          * forever.  So convert it to the real root.                           * forever.  So convert it to the real root.
                          */                           */
                         if (searchdir != NULL) {                          if (searchdir != NULL) {
                                 if (searchdir == foundobj)                                  if (searchdir_locked) {
                                         vrele(searchdir);  
                                 else  
                                         vput(searchdir);                                          vput(searchdir);
                                           searchdir_locked = false;
                                   } else {
                                           vrele(searchdir);
                                   }
                                 searchdir = NULL;                                  searchdir = NULL;
                         }                          }
                         vput(foundobj);                          vrele(foundobj);
                         foundobj = ndp->ni_rootdir;                          foundobj = ndp->ni_rootdir;
                         vref(foundobj);                          vref(foundobj);
                         vn_lock(foundobj, LK_EXCLUSIVE | LK_RETRY);  
                 }                  }
   
                 /*                  /*
Line 1439  namei_oneroot(struct namei_state *state,
Line 1686  namei_oneroot(struct namei_state *state,
                     (searchdir == NULL ||                      (searchdir == NULL ||
                      searchdir->v_mount != foundobj->v_mount)) {                       searchdir->v_mount != foundobj->v_mount)) {
                         if (searchdir) {                          if (searchdir) {
                                 vput(searchdir);                                  if (searchdir_locked) {
                                           vput(searchdir);
                                           searchdir_locked = false;
                                   } else {
                                           vrele(searchdir);
                                   }
                                   searchdir = NULL;
                         }                          }
                         vput(foundobj);                          vrele(foundobj);
                         foundobj = NULL;                          foundobj = NULL;
                         ndp->ni_dvp = NULL;                          ndp->ni_dvp = NULL;
                         ndp->ni_vp = NULL;                          ndp->ni_vp = NULL;
Line 1466  namei_oneroot(struct namei_state *state,
Line 1719  namei_oneroot(struct namei_state *state,
                 if (state->rdonly &&                  if (state->rdonly &&
                     (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {                      (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
                         if (searchdir) {                          if (searchdir) {
                                 if (foundobj != searchdir) {                                  if (searchdir_locked) {
                                         vput(searchdir);                                          vput(searchdir);
                                           searchdir_locked = false;
                                 } else {                                  } else {
                                         vrele(searchdir);                                          vrele(searchdir);
                                 }                                  }
                                 searchdir = NULL;                                  searchdir = NULL;
                         }                          }
                         vput(foundobj);                          vrele(foundobj);
                         foundobj = NULL;                          foundobj = NULL;
                         ndp->ni_dvp = NULL;                          ndp->ni_dvp = NULL;
                         ndp->ni_vp = NULL;                          ndp->ni_vp = NULL;
                         state->attempt_retry = 1;                          state->attempt_retry = 1;
                         return EROFS;                          return EROFS;
                 }                  }
                 if ((cnp->cn_flags & LOCKLEAF) == 0) {  
                   /* Lock the leaf node if requested. */
                   if ((cnp->cn_flags & (LOCKLEAF | LOCKPARENT)) == LOCKPARENT &&
                       searchdir == foundobj) {
                         /*                          /*
                          * Note: if LOCKPARENT but not LOCKLEAF is                           * Note: if LOCKPARENT but not LOCKLEAF is
                          * set, and searchdir == foundobj, this code                           * set, and searchdir == foundobj, this code
Line 1492  namei_oneroot(struct namei_state *state,
Line 1749  namei_oneroot(struct namei_state *state,
                          * that uses this combination "knows" this, so                           * that uses this combination "knows" this, so
                          * it can't be safely changed. Feh. XXX                           * it can't be safely changed. Feh. XXX
                          */                           */
                         VOP_UNLOCK(foundobj);                          KASSERT(searchdir_locked);
                           VOP_UNLOCK(searchdir);
                           searchdir_locked = false;
                   } else if ((cnp->cn_flags & LOCKLEAF) != 0 &&
                       (searchdir != foundobj ||
                       (cnp->cn_flags & LOCKPARENT) == 0)) {
                           const int lktype = (cnp->cn_flags & LOCKSHARED) != 0 ?
                               LK_SHARED : LK_EXCLUSIVE;
                           vn_lock(foundobj, lktype | LK_RETRY);
                 }                  }
         }          }
   
Line 1504  namei_oneroot(struct namei_state *state,
Line 1769  namei_oneroot(struct namei_state *state,
          * If LOCKPARENT is not set, the parent directory isn't returned.           * If LOCKPARENT is not set, the parent directory isn't returned.
          */           */
         if ((cnp->cn_flags & LOCKPARENT) == 0 && searchdir != NULL) {          if ((cnp->cn_flags & LOCKPARENT) == 0 && searchdir != NULL) {
                 if (searchdir == foundobj) {                  vrele(searchdir);
                         vrele(searchdir);  
                 } else {  
                         vput(searchdir);  
                 }  
                 searchdir = NULL;                  searchdir = NULL;
         }          }
   
Line 1650  do_lookup_for_nfsd_index(struct namei_st
Line 1911  do_lookup_for_nfsd_index(struct namei_st
         struct nameidata *ndp = state->ndp;          struct nameidata *ndp = state->ndp;
         struct vnode *startdir;          struct vnode *startdir;
         struct vnode *foundobj;          struct vnode *foundobj;
           bool startdir_locked;
         const char *cp;                 /* pointer into pathname argument */          const char *cp;                 /* pointer into pathname argument */
   
         KASSERT(cnp == &ndp->ni_cnd);          KASSERT(cnp == &ndp->ni_cnd);
Line 1682  do_lookup_for_nfsd_index(struct namei_st
Line 1944  do_lookup_for_nfsd_index(struct namei_st
          * own reference to it to avoid consuming the caller's.           * own reference to it to avoid consuming the caller's.
          */           */
         vref(startdir);          vref(startdir);
         vn_lock(startdir, LK_EXCLUSIVE | LK_RETRY);          error = lookup_once(state, startdir, &startdir, &foundobj,
         error = lookup_once(state, startdir, &startdir, &foundobj);              &startdir_locked);
         if (error == 0 && startdir == foundobj) {  
                 vrele(startdir);  
         } else if (startdir != NULL) {  
                 vput(startdir);  
         }  
         if (error) {  
                 goto bad;  
         }  
         ndp->ni_vp = foundobj;  
   
         if (foundobj == NULL) {          KASSERT((cnp->cn_flags & LOCKPARENT) == 0);
                 return 0;          if (startdir_locked) {
                   VOP_UNLOCK(startdir);
                   startdir_locked = false;
         }          }
   
         KASSERT((cnp->cn_flags & LOCKPARENT) == 0);          /*
         if ((cnp->cn_flags & LOCKLEAF) == 0) {           * If the vnode we found is mounted on, then cross the mount and get
                 VOP_UNLOCK(foundobj);           * the root vnode in foundobj.  If this encounters an error, it will
            * dispose of foundobj, but searchdir is untouched.
            */
           if (error == 0 && foundobj != NULL &&
               foundobj->v_type == VDIR &&
               foundobj->v_mountedhere != NULL &&
               (cnp->cn_flags & NOCROSSMOUNT) == 0) {
                   error = lookup_crossmount(state, &startdir, &foundobj,
                       &startdir_locked);
         }          }
         return (0);  
   
 bad:          /* Now toss startdir and see if we have an error. */
         ndp->ni_vp = NULL;          if (startdir != NULL)
                   vrele(startdir);
           if (error)
                   foundobj = NULL;
           else if (foundobj != NULL && (cnp->cn_flags & LOCKLEAF) != 0)
                   vn_lock(foundobj, LK_EXCLUSIVE | LK_RETRY);
   
           ndp->ni_vp = foundobj;
         return (error);          return (error);
 }  }
   

Legend:
Removed from v.1.212  
changed lines
  Added in v.1.212.4.9

CVSweb <webmaster@jp.NetBSD.org>