]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/vfs/vfs_lookup.c
xnu-6153.141.1.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_lookup.c
index 85d47741ee3c9fea99a9709b9d45e49bd584b61d..9aad31d57bc851436c038190d84ca638c906de37 100644 (file)
@@ -113,7 +113,7 @@ static int vfs_getrealpath(const char * path, char * realpath, size_t bufsize, v
 #endif
 
 static int              lookup_traverse_mountpoints(struct nameidata *ndp, struct componentname *cnp, vnode_t dp, int vbusyflags, vfs_context_t ctx);
-static int              handle_symlink_for_namei(struct nameidata *ndp, vnode_t *new_dp, vfs_context_t ctx);
+static int              lookup_handle_symlink(struct nameidata *ndp, vnode_t *new_dp, vfs_context_t ctx);
 static int              lookup_authorize_search(vnode_t dp, struct componentname *cnp, int dp_authorized_in_cache, vfs_context_t ctx);
 static void             lookup_consider_update_cache(vnode_t dvp, vnode_t vp, struct componentname *cnp, int nc_generation);
 static int              lookup_handle_found_vnode(struct nameidata *ndp, struct componentname *cnp, int rdonly,
@@ -167,8 +167,6 @@ namei(struct nameidata *ndp)
 {
        struct filedesc *fdp;   /* pointer to file descriptor state */
        struct vnode *dp;       /* the directory we are searching */
-       struct vnode *rootdir_with_usecount = NULLVP;
-       struct vnode *startdir_with_usecount = NULLVP;
        struct vnode *usedvp = ndp->ni_dvp;  /* store pointer to vp in case we must loop due to
                                              *                                          heavy vnode pressure */
        u_long cnpflags = ndp->ni_cnd.cn_flags; /* store in case we have to restore after loop */
@@ -185,6 +183,8 @@ namei(struct nameidata *ndp)
        int volfs_restarts = 0;
 #endif
        size_t bytes_copied = 0;
+       bool take_proc_lock = !(ndp->ni_flag & NAMEI_NOPROCLOCK);
+       bool proc_lock_taken = false;
 
        fdp = p->p_fd;
 
@@ -351,29 +351,15 @@ retry_copy:
        /*
         * determine the starting point for the translation.
         *
-        * We may need to upto 2 usecounts on vnodes before starting the translation
-        * We need to have a usecount on the root directory for the process
-        * for the entire duration of the lookup. This is because symlink
-        * translation can restart translation at / if a symlink is encountered.
-        *
-        * For the duration of this lookup at rootdir for this lookup is the one
-        * we fetch now under the proc_fdlock even the if the proc rootdir changes
-        * once we let go of the proc_fdlock.
-        *
-        * In the future we may consider holding off a chroot till we complete
-        * in progress lookups.
-        *
-        * If the starting directory is not the process rootdir then we need
-        * a usecount on the starting directory as well for the duration of the
-        * lookup.
-        *
-        * Getting an addtional usecount involves first getting an iocount under
-        * the lock that ensures that a usecount is on the directory. Once we
-        * get an iocount we can release the lock and we will be free to get a
-        * usecount without the vnode getting recycled. Once we get the usecount
-        * we can release the icoount which we used to get our usecount.
+        * We hold the proc_dirs lock across the lookup so that the
+        * process rootdir and cwd are stable (i.e. the usecounts
+        * on them are mainatained for the duration of the lookup)
         */
-       proc_fdlock(p);
+       if (take_proc_lock) {
+               assert(proc_lock_taken == false);
+               proc_dirs_lock_shared(p);
+               proc_lock_taken = true;
+       }
        if (!(fdp->fd_flags & FD_CHROOT)) {
                ndp->ni_rootdir = rootvnode;
        } else {
@@ -382,10 +368,8 @@ retry_copy:
 
        if (!ndp->ni_rootdir) {
                if (!(fdp->fd_flags & FD_CHROOT)) {
-                       proc_fdunlock(p);
                        printf("rootvnode is not set\n");
                } else {
-                       proc_fdunlock(p);
                        /* This should be a panic */
                        printf("fdp->fd_rdir is not set\n");
                }
@@ -393,43 +377,10 @@ retry_copy:
                goto error_out;
        }
 
-       /*
-        * We have the proc_fdlock here so we still have a usecount
-        * on ndp->ni_rootdir.
-        *
-        * However we need to get our own usecount on it in order to
-        * ensure that the vnode isn't recycled to something else.
-        *
-        * Note : It's fine if the vnode is force reclaimed but with
-        * a usecount it won't be reused until we release the reference.
-        *
-        * In order to get that usecount however, we need to first
-        * get non blocking iocount since we'll be doing this under
-        * the proc_fdlock.
-        */
-       if (vnode_get(ndp->ni_rootdir) != 0) {
-               proc_fdunlock(p);
-               error = ENOENT;
-               goto error_out;
-       }
-
-       proc_fdunlock(p);
-
-       /* Now we can safely get our own ref on ni_rootdir */
-       error = vnode_ref_ext(ndp->ni_rootdir, O_EVTONLY, 0);
-       vnode_put(ndp->ni_rootdir);
-       if (error) {
-               ndp->ni_rootdir = NULLVP;
-               goto error_out;
-       }
-
-       rootdir_with_usecount = ndp->ni_rootdir;
-
        cnp->cn_nameptr = cnp->cn_pnbuf;
 
        ndp->ni_usedvp = NULLVP;
 
-       bool dp_needs_put = false;
        if (*(cnp->cn_nameptr) == '/') {
                while (*(cnp->cn_nameptr) == '/') {
                        cnp->cn_nameptr++;
@@ -440,40 +391,15 @@ retry_copy:
                dp = ndp->ni_dvp;
                ndp->ni_usedvp = dp;
        } else {
-               dp = vfs_context_get_cwd(ctx);
-               if (dp) {
-                       dp_needs_put = true;
-               }
+               dp = vfs_context_cwd(ctx);
        }
 
        if (dp == NULLVP || (dp->v_lflag & VL_DEAD)) {
-               if (dp_needs_put) {
-                       vnode_put(dp);
-                       dp_needs_put = false;
-               }
                dp = NULLVP;
                error = ENOENT;
                goto error_out;
        }
 
-       if (dp != rootdir_with_usecount) {
-               error = vnode_ref_ext(dp, O_EVTONLY, 0);
-               if (error) {
-                       if (dp_needs_put) {
-                               vnode_put(dp);
-                               dp_needs_put = false;
-                       }
-                       dp = NULLVP;
-                       goto error_out;
-               }
-               startdir_with_usecount = dp;
-       }
-
-       if (dp_needs_put) {
-               vnode_put(dp);
-               dp_needs_put = false;
-       }
-
        ndp->ni_dvp = NULLVP;
        ndp->ni_vp  = NULLVP;
 
@@ -492,7 +418,6 @@ retry_copy:
                        goto error_out;
                }
 #endif
-
                ndp->ni_startdir = dp;
                dp = NULLVP;
 
@@ -504,46 +429,19 @@ retry_copy:
                 * Check for symbolic link
                 */
                if ((cnp->cn_flags & ISSYMLINK) == 0) {
-                       if (startdir_with_usecount) {
-                               vnode_rele_ext(startdir_with_usecount, O_EVTONLY, 0);
-                               startdir_with_usecount = NULLVP;
-                       }
-                       if (rootdir_with_usecount) {
-                               vnode_rele_ext(rootdir_with_usecount, O_EVTONLY, 0);
-                               rootdir_with_usecount = NULLVP;
+                       if (proc_lock_taken) {
+                               proc_dirs_unlock_shared(p);
+                               proc_lock_taken = false;
                        }
                        return 0;
                }
 
 continue_symlink:
-               /*
-                * Gives us a new path to process, and a starting dir (with an iocount).
-                * The iocount is needed to take a usecount on the vnode returned
-                * (if it is not a vnode we already have a usecount on).
-                */
-               error = handle_symlink_for_namei(ndp, &dp, ctx);
+               /* Gives us a new path to process, and a starting dir */
+               error = lookup_handle_symlink(ndp, &dp, ctx);
                if (error != 0) {
                        break;
                }
-
-               if (dp == ndp->ni_rootdir && startdir_with_usecount) {
-                       vnode_rele_ext(startdir_with_usecount, O_EVTONLY, 0);
-                       startdir_with_usecount = NULLVP;
-               } else if (dp != startdir_with_usecount) {
-                       if (startdir_with_usecount) {
-                               vnode_rele_ext(startdir_with_usecount, O_EVTONLY, 0);
-                               startdir_with_usecount = NULLVP;
-                       }
-                       error = vnode_ref_ext(dp, O_EVTONLY, 0);
-                       if (error) {
-                               vnode_put(dp);
-                               dp = NULLVP;
-                               goto error_out;
-                       }
-                       startdir_with_usecount = dp;
-               }
-               /* iocount not required on dp anymore */
-               vnode_put(dp);
        }
        /*
         * only come here if we fail to handle a SYMLINK...
@@ -559,6 +457,10 @@ out_drop:
                vnode_put(ndp->ni_vp);
        }
 error_out:
+       if (proc_lock_taken) {
+               proc_dirs_unlock_shared(p);
+               proc_lock_taken = false;
+       }
        if ((cnp->cn_flags & HASBUF)) {
                cnp->cn_flags &= ~HASBUF;
                FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
@@ -567,15 +469,6 @@ error_out:
        ndp->ni_vp = NULLVP;
        ndp->ni_dvp = NULLVP;
 
-       if (startdir_with_usecount) {
-               vnode_rele_ext(startdir_with_usecount, O_EVTONLY, 0);
-               startdir_with_usecount = NULLVP;
-       }
-       if (rootdir_with_usecount) {
-               vnode_rele_ext(rootdir_with_usecount, O_EVTONLY, 0);
-               rootdir_with_usecount = NULLVP;
-       }
-
 #if CONFIG_VOLFS
        /*
         * Deal with volfs fallout.
@@ -1261,13 +1154,15 @@ dirloop:
                        tdp = dp;
                        dp = tdp->v_mount->mnt_vnodecovered;
 
-                       vnode_put(tdp);
-
                        if ((vnode_getwithref(dp))) {
+                               vnode_put(tdp);
                                dp = NULLVP;
                                error = ENOENT;
                                goto bad;
                        }
+
+                       vnode_put(tdp);
+
                        ndp->ni_dvp = dp;
                        dp_authorized = 0;
                }
@@ -1670,10 +1565,10 @@ out:
 
 /*
  * Takes ni_vp and ni_dvp non-NULL.  Returns with *new_dp set to the location
- * at which to start a lookup with a resolved path and with an iocount.
+ * at which to start a lookup with a resolved path, and all other iocounts dropped.
  */
 static int
-handle_symlink_for_namei(struct nameidata *ndp, vnode_t *new_dp, vfs_context_t ctx)
+lookup_handle_symlink(struct nameidata *ndp, vnode_t *new_dp, vfs_context_t ctx)
 {
        int error;
        char *cp;               /* pointer into pathname argument */
@@ -1764,18 +1659,17 @@ handle_symlink_for_namei(struct nameidata *ndp, vnode_t *new_dp, vfs_context_t c
        /*
         * starting point for 'relative'
         * symbolic link path
-        *
-        * If the starting point is not the root we have to return an iocounted
-        * dp to namei so we don't release the icoount here.
         */
        dp = ndp->ni_dvp;
-       ndp->ni_dvp = NULLVP;
 
        /*
         * get rid of references returned via 'lookup'
         */
        vnode_put(ndp->ni_vp);
+       vnode_put(ndp->ni_dvp); /* ALWAYS have a dvp for a symlink */
+
        ndp->ni_vp = NULLVP;
+       ndp->ni_dvp = NULLVP;
 
        /*
         * Check if symbolic link restarts us at the root
@@ -1785,20 +1679,9 @@ handle_symlink_for_namei(struct nameidata *ndp, vnode_t *new_dp, vfs_context_t c
                        cnp->cn_nameptr++;
                        ndp->ni_pathlen--;
                }
-               vnode_put(dp);
                if ((dp = ndp->ni_rootdir) == NULLVP) {
                        return ENOENT;
                }
-               if (vnode_get(dp) != 0) {
-                       return ENOENT;
-               }
-       }
-
-       if (dp == NULLVP || (dp->v_lflag & VL_DEAD)) {
-               if (dp) {
-                       vnode_put(dp);
-               }
-               return ENOENT;
        }
 
        *new_dp = dp;