#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,
{
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 */
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;
/*
* 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 {
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");
}
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++;
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;
goto error_out;
}
#endif
-
ndp->ni_startdir = dp;
dp = NULLVP;
* 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...
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);
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.
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;
}
/*
* 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 */
/*
* 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
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;