#include <sys/vnode_internal.h>
#include <sys/mount_internal.h>
#include <sys/errno.h>
-#include <sys/malloc.h>
+#include <kern/kalloc.h>
#include <sys/filedesc.h>
#include <sys/proc_internal.h>
#include <sys/kdebug.h>
#include <sys/unistd.h> /* For _PC_NAME_MAX */
#include <sys/uio_internal.h>
#include <sys/kauth.h>
-#include <kern/kalloc.h>
+#include <kern/zalloc.h>
#include <security/audit/audit.h>
-#include <sys/dtrace.h> /* to get the prototype for strstr() in sys/dtrace_glue.h */
#if CONFIG_MACF
#include <security/mac_framework.h>
#endif
+#include <os/atomic_private.h>
#include <sys/paths.h>
#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, bool* dp_has_iocount, 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,
static int lookup_handle_rsrc_fork(vnode_t dp, struct nameidata *ndp, struct componentname *cnp, int wantparent, vfs_context_t ctx);
#endif
-
+extern lck_rw_t rootvnode_rw_lock;
/*
* Convert a pathname into a pointer to a locked inode.
{
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 */
+ uint32_t cnpflags = ndp->ni_cnd.cn_flags; /* store in case we have to restore after loop */
int error;
struct componentname *cnp = &ndp->ni_cnd;
vfs_context_t ctx = cnp->cn_context;
int volfs_restarts = 0;
#endif
size_t bytes_copied = 0;
+ vnode_t rootdir_with_usecount = NULLVP;
+ vnode_t startdir_with_usecount = NULLVP;
+ vnode_t usedvp_dp = NULLVP;
+ int32_t old_count = 0;
+ bool dp_has_iocount = false;
fdp = p->p_fd;
cnp->cn_pnlen, &bytes_copied);
}
if (error == ENAMETOOLONG && !(cnp->cn_flags & HASBUF)) {
- MALLOC_ZONE(cnp->cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
- if (cnp->cn_pnbuf == NULL) {
- error = ENOMEM;
- goto error_out;
- }
-
+ cnp->cn_pnbuf = zalloc(ZV_NAMEI);
cnp->cn_flags |= HASBUF;
cnp->cn_pnlen = MAXPATHLEN;
bytes_copied = 0;
if (error) {
goto error_out;
}
- ndp->ni_pathlen = bytes_copied;
+ assert(bytes_copied <= MAXPATHLEN);
+ ndp->ni_pathlen = (u_int)bytes_copied;
bytes_copied = 0;
/*
char * realpath;
int realpath_err;
/* Attempt to resolve a legacy volfs style pathname. */
- MALLOC_ZONE(realpath, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
- if (realpath) {
- /*
- * We only error out on the ENAMETOOLONG cases where we know that
- * vfs_getrealpath translation succeeded but the path could not fit into
- * MAXPATHLEN characters. In other failure cases, we may be dealing with a path
- * that legitimately looks like /.vol/1234/567 and is not meant to be translated
- */
- if ((realpath_err = vfs_getrealpath(&cnp->cn_pnbuf[6], realpath, MAXPATHLEN, ctx))) {
- FREE_ZONE(realpath, MAXPATHLEN, M_NAMEI);
- if (realpath_err == ENOSPC || realpath_err == ENAMETOOLONG) {
- error = ENAMETOOLONG;
- goto error_out;
- }
- } else {
- if (cnp->cn_flags & HASBUF) {
- FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
- }
- cnp->cn_pnbuf = realpath;
- cnp->cn_pnlen = MAXPATHLEN;
- ndp->ni_pathlen = strlen(realpath) + 1;
- cnp->cn_flags |= HASBUF | CN_VOLFSPATH;
+ realpath = zalloc(ZV_NAMEI);
+ /*
+ * We only error out on the ENAMETOOLONG cases where we know that
+ * vfs_getrealpath translation succeeded but the path could not fit into
+ * MAXPATHLEN characters. In other failure cases, we may be dealing with a path
+ * that legitimately looks like /.vol/1234/567 and is not meant to be translated
+ */
+ if ((realpath_err = vfs_getrealpath(&cnp->cn_pnbuf[6], realpath, MAXPATHLEN, ctx))) {
+ zfree(ZV_NAMEI, realpath);
+ if (realpath_err == ENOSPC || realpath_err == ENAMETOOLONG) {
+ error = ENAMETOOLONG;
+ goto error_out;
+ }
+ } else {
+ size_t tmp_len;
+ if (cnp->cn_flags & HASBUF) {
+ zfree(ZV_NAMEI, cnp->cn_pnbuf);
}
+ cnp->cn_pnbuf = realpath;
+ cnp->cn_pnlen = MAXPATHLEN;
+ tmp_len = strlen(realpath) + 1;
+ assert(tmp_len <= UINT_MAX);
+ ndp->ni_pathlen = (u_int)tmp_len;
+ cnp->cn_flags |= HASBUF | CN_VOLFSPATH;
}
}
#endif /* CONFIG_VOLFS */
error = ENOENT;
goto error_out;
}
- ndp->ni_loopcnt = 0;
+ if (ndp->ni_flag & NAMEI_NOFOLLOW_ANY) {
+ ndp->ni_loopcnt = MAXSYMLINKS;
+ } else {
+ ndp->ni_loopcnt = 0;
+ }
/*
* 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.
*/
- proc_fdlock(p);
+ proc_dirs_lock_shared(p);
+ lck_rw_lock_shared(&rootvnode_rw_lock);
+
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");
}
+ lck_rw_unlock_shared(&rootvnode_rw_lock);
+ proc_dirs_unlock_shared(p);
error = ENOENT;
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++;
} else if (cnp->cn_flags & USEDVP) {
dp = ndp->ni_dvp;
ndp->ni_usedvp = dp;
+ usedvp_dp = 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;
+ lck_rw_unlock_shared(&rootvnode_rw_lock);
+ proc_dirs_unlock_shared(p);
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;
+ /*
+ * We need our own usecount on the root vnode and the starting dir across
+ * the lookup. There's two things that be done here. We can hold the locks
+ * (which protect the existing usecounts on the directories) across the
+ * lookup or take our own usecount. Holding the locks across the lookup can
+ * cause deadlock issues if we re-enter namei on the same thread so the
+ * correct thing to do is to acquire our own usecount.
+ *
+ * Ideally, the usecount should be obtained by vnode_get->vnode_ref->vnode_put.
+ * However when this vnode is the rootvnode, that sequence will produce a
+ * lot of vnode mutex locks and unlocks on a single vnode (the rootvnode)
+ * and will be highly contended and degrade performance. Since we have
+ * an existing usecount protected by the locks we hold, we'll just use
+ * an atomic op to increment the usecount on a vnode which already has one
+ * and can't be released becasue we have the locks which protect against that
+ * happening.
+ */
+ rootdir_with_usecount = ndp->ni_rootdir;
+ old_count = os_atomic_inc_orig(&rootdir_with_usecount->v_usecount, relaxed);
+ if (old_count < 1) {
+ panic("(1) invalid pre-increment usecount (%d) for rootdir vnode %p",
+ old_count, rootdir_with_usecount);
+ } else if (old_count == INT32_MAX) {
+ panic("(1) usecount overflow for vnode %p", rootdir_with_usecount);
+ }
+
+ if ((dp != rootdir_with_usecount) && (dp != usedvp_dp)) {
+ old_count = os_atomic_inc_orig(&dp->v_usecount, relaxed);
+ if (old_count < 1) {
+ panic("(2) invalid pre-increment usecount (%d) for vnode %p", old_count, dp);
+ } else if (old_count == INT32_MAX) {
+ panic("(2) usecount overflow for vnode %p", dp);
}
startdir_with_usecount = dp;
}
- if (dp_needs_put) {
- vnode_put(dp);
- dp_needs_put = false;
- }
+ /* Now that we have our usecount, release the locks */
+ lck_rw_unlock_shared(&rootvnode_rw_lock);
+ proc_dirs_unlock_shared(p);
ndp->ni_dvp = NULLVP;
ndp->ni_vp = NULLVP;
goto error_out;
}
#endif
-
ndp->ni_startdir = dp;
dp = NULLVP;
*/
if ((cnp->cn_flags & ISSYMLINK) == 0) {
if (startdir_with_usecount) {
- vnode_rele_ext(startdir_with_usecount, O_EVTONLY, 0);
+ vnode_rele(startdir_with_usecount);
startdir_with_usecount = NULLVP;
}
if (rootdir_with_usecount) {
- vnode_rele_ext(rootdir_with_usecount, O_EVTONLY, 0);
- rootdir_with_usecount = NULLVP;
+ lck_rw_lock_shared(&rootvnode_rw_lock);
+ if (rootdir_with_usecount == rootvnode) {
+ old_count = os_atomic_dec_orig(&rootdir_with_usecount->v_usecount, relaxed);
+ if (old_count < 2) {
+ /*
+ * There needs to have been at least 1 usecount left on the rootvnode
+ */
+ panic("(3) Unexpected pre-decrement value (%d) of usecount for rootvnode %p",
+ old_count, rootdir_with_usecount);
+ }
+ rootdir_with_usecount = NULLVP;
+ }
+ lck_rw_unlock_shared(&rootvnode_rw_lock);
+ if (rootdir_with_usecount) {
+ vnode_rele(rootdir_with_usecount);
+ rootdir_with_usecount = NULLVP;
+ }
}
+
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, &dp_has_iocount, 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;
+ if (dp_has_iocount) {
+ if ((dp != rootdir_with_usecount) && (dp != startdir_with_usecount) &&
+ (dp != usedvp_dp)) {
+ if (startdir_with_usecount) {
+ vnode_rele(startdir_with_usecount);
+ }
+ vnode_ref_ext(dp, 0, VNODE_REF_FORCE);
+ startdir_with_usecount = dp;
}
- startdir_with_usecount = dp;
+ vnode_put(dp);
+ dp_has_iocount = false;
}
- /* 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 ((cnp->cn_flags & HASBUF)) {
- cnp->cn_flags &= ~HASBUF;
- FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
- }
- cnp->cn_pnbuf = NULL;
- ndp->ni_vp = NULLVP;
- ndp->ni_dvp = NULLVP;
-
if (startdir_with_usecount) {
- vnode_rele_ext(startdir_with_usecount, O_EVTONLY, 0);
+ vnode_rele(startdir_with_usecount);
startdir_with_usecount = NULLVP;
}
if (rootdir_with_usecount) {
- vnode_rele_ext(rootdir_with_usecount, O_EVTONLY, 0);
+ lck_rw_lock_shared(&rootvnode_rw_lock);
+ if (rootdir_with_usecount == rootvnode) {
+ old_count = os_atomic_dec_orig(&rootdir_with_usecount->v_usecount, relaxed);
+ if (old_count < 2) {
+ /*
+ * There needs to have been at least 1 usecount left on the rootvnode
+ */
+ panic("(4) Unexpected pre-decrement value (%d) of usecount for rootvnode %p",
+ old_count, rootdir_with_usecount);
+ }
+ lck_rw_unlock_shared(&rootvnode_rw_lock);
+ } else {
+ lck_rw_unlock_shared(&rootvnode_rw_lock);
+ vnode_rele(rootdir_with_usecount);
+ }
rootdir_with_usecount = NULLVP;
}
+ if ((cnp->cn_flags & HASBUF)) {
+ cnp->cn_flags &= ~HASBUF;
+ zfree(ZV_NAMEI, cnp->cn_pnbuf);
+ }
+ cnp->cn_pnbuf = NULL;
+ ndp->ni_vp = NULLVP;
+ ndp->ni_dvp = 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;
}
{
char *path = NULL, *pp;
const char *name, *np;
- int len;
+ size_t len;
int error = 0;
struct nameidata nd;
vnode_t vp = dvp;
return 0;
}
- path = (char *) kalloc(MAXPATHLEN);
+ path = (char *) zalloc(ZV_NAMEI);
if (path == NULL) {
error = ENOMEM;
goto done;
goto done;
}
len = strlen(name);
- if ((len + 1) > (pp - path)) { // Enough space for this name ?
+ if ((len + 1) > (size_t)(pp - path)) { // Enough space for this name ?
error = ENAMETOOLONG;
vnode_putname(name);
goto done;
nameidone(&nd);
done:
if (path) {
- kfree(path, MAXPATHLEN);
+ zfree(ZV_NAMEI, path);
}
return error;
}
/*
* 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, bool *new_dp_has_iocount, vfs_context_t ctx)
{
int error;
char *cp; /* pointer into pathname argument */
vnode_t dp;
char *tmppn;
u_int rsrclen = (cnp->cn_flags & CN_WANTSRSRCFORK) ? sizeof(_PATH_RSRCFORKSPEC) : 0;
+ bool dp_has_iocount = false;
if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
return ELOOP;
}
if (need_newpathbuf) {
- MALLOC_ZONE(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
- if (cp == NULL) {
- return ENOMEM;
- }
+ cp = zalloc(ZV_NAMEI);
} else {
cp = cnp->cn_pnbuf;
}
error = VNOP_READLINK(ndp->ni_vp, auio, ctx);
if (error) {
if (need_newpathbuf) {
- FREE_ZONE(cp, MAXPATHLEN, M_NAMEI);
+ zfree(ZV_NAMEI, cp);
}
return error;
}
linklen = MAXPATHLEN - (u_int)uio_resid(auio);
if (linklen + ndp->ni_pathlen + rsrclen > MAXPATHLEN) {
if (need_newpathbuf) {
- FREE_ZONE(cp, MAXPATHLEN, M_NAMEI);
+ zfree(ZV_NAMEI, cp);
}
return ENAMETOOLONG;
}
if (need_newpathbuf) {
- long len = cnp->cn_pnlen;
-
tmppn = cnp->cn_pnbuf;
bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
cnp->cn_pnbuf = cp;
cnp->cn_pnlen = MAXPATHLEN;
if ((cnp->cn_flags & HASBUF)) {
- FREE_ZONE(tmppn, len, M_NAMEI);
+ zfree(ZV_NAMEI, tmppn);
} else {
cnp->cn_flags |= HASBUF;
}
/*
* 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'
+ * get rid of reference returned via 'lookup'
+ * ni_dvp is released only if we restart at /.
*/
vnode_put(ndp->ni_vp);
ndp->ni_vp = NULLVP;
+ ndp->ni_dvp = NULLVP;
/*
* Check if symbolic link restarts us at the root
*/
if (*(cnp->cn_nameptr) == '/') {
+ vnode_put(dp); /* ALWAYS have a dvp for a symlink */
while (*(cnp->cn_nameptr) == '/') {
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;
+ } else {
+ dp_has_iocount = true;
}
*new_dp = dp;
+ *new_dp_has_iocount = dp_has_iocount;
return 0;
}
ndp->ni_cnd.cn_pnbuf = NULL;
ndp->ni_cnd.cn_flags &= ~HASBUF;
- FREE_ZONE(tmp, ndp->ni_cnd.cn_pnlen, M_NAMEI);
+ zfree(ZV_NAMEI, tmp);
}
}
/*
- * Log (part of) a pathname using the KERNEL_DEBUG_CONSTANT mechanism, as used
- * by fs_usage. The path up to and including the current component name are
- * logged. Up to NUMPARMS*4 bytes of pathname will be logged. If the path
- * to be logged is longer than that, then the last NUMPARMS*4 bytes are logged.
- * That is, the truncation removes the leading portion of the path.
- *
- * The logging is done via multiple KERNEL_DEBUG_CONSTANT calls. The first one
- * is marked with DBG_FUNC_START. The last one is marked with DBG_FUNC_END
- * (in addition to DBG_FUNC_START if it is also the first). There may be
- * intermediate ones with neither DBG_FUNC_START nor DBG_FUNC_END.
+ * Log (part of) a pathname using kdebug, as used by fs_usage. The path up to
+ * and including the current component name are logged. Up to NUMPARMS * 4
+ * bytes of pathname will be logged. If the path to be logged is longer than
+ * that, then the last NUMPARMS * 4 bytes are logged. That is, the truncation
+ * removes the leading portion of the path.
*
- * The first KERNEL_DEBUG_CONSTANT passes the vnode pointer and 12 bytes of
- * pathname. The remaining KERNEL_DEBUG_CONSTANT calls add 16 bytes of pathname
- * each. The minimum number of KERNEL_DEBUG_CONSTANT calls required to pass
- * the path are used. Any excess padding in the final KERNEL_DEBUG_CONSTANT
- * (because not all of the 12 or 16 bytes are needed for the remainder of the
- * path) is set to zero bytes, or '>' if there is more path beyond the
- * current component name (usually because an intermediate component was not
- * found).
+ * The logging is done via multiple KDBG_RELEASE calls. The first one is marked
+ * with DBG_FUNC_START. The last one is marked with DBG_FUNC_END (in addition
+ * to DBG_FUNC_START if it is also the first). There may be intermediate ones
+ * with neither DBG_FUNC_START nor DBG_FUNC_END.
*
- * NOTE: If the path length is greater than NUMPARMS*4, or is not of the form
- * 12+N*16, there will be no padding.
+ * The first event passes the vnode pointer and 24 or 32 (on K32, 12 or 24)
+ * bytes of pathname. The remaining events add 32 (on K32, 16) bytes of
+ * pathname each. The minimum number of events required to pass the path are
+ * used. Any excess padding in the final event (because not all of the 24 or 32
+ * (on K32, 12 or 16) bytes are needed for the remainder of the path) is set to
+ * zero bytes, or '>' if there is more path beyond the current component name
+ * (usually because an intermediate component was not found).
*
- * TODO: If there is more path beyond the current component name, should we
- * force some padding? For example, a lookup for /foo_bar_baz/spam that
- * fails because /foo_bar_baz is not found will only log "/foo_bar_baz", with
- * no '>' padding. But /foo_bar/spam would log "/foo_bar>>>>".
+ * NOTE: If the path length is greater than NUMPARMS * 4, or is not of the form
+ * 24 + N * 32 (or on K32, 12 + N * 16), there will be no padding.
*/
#if (KDEBUG_LEVEL >= KDEBUG_LEVEL_IST)
void
-kdebug_vfs_lookup(long *dbg_parms, int dbg_namelen, void *dp, uint32_t flags)
+kdebug_vfs_lookup(unsigned long *path_words, int path_len, void *vnp,
+ uint32_t flags)
{
- int code;
- unsigned int i;
- bool lookup = flags & KDBG_VFS_LOOKUP_FLAG_LOOKUP;
bool noprocfilt = flags & KDBG_VFS_LOOKUP_FLAG_NOPROCFILT;
- /*
- * In the event that we collect multiple, consecutive pathname
- * entries, we must mark the start of the path's string and the end.
- */
- if (lookup) {
- code = VFS_LOOKUP | DBG_FUNC_START;
- } else {
- code = VFS_LOOKUP_DONE | DBG_FUNC_START;
- }
+ assert(path_len >= 0);
- if (dbg_namelen <= (int)(3 * sizeof(long))) {
+ int code = ((flags & KDBG_VFS_LOOKUP_FLAG_LOOKUP) ? VFS_LOOKUP :
+ VFS_LOOKUP_DONE) | DBG_FUNC_START;
+
+ if (path_len <= (3 * (int)sizeof(long))) {
code |= DBG_FUNC_END;
}
if (noprocfilt) {
- KDBG_RELEASE_NOPROCFILT(code, kdebug_vnode(dp), dbg_parms[0],
- dbg_parms[1], dbg_parms[2]);
+ KDBG_RELEASE_NOPROCFILT(code, kdebug_vnode(vnp), path_words[0],
+ path_words[1], path_words[2]);
} else {
- KDBG_RELEASE(code, kdebug_vnode(dp), dbg_parms[0], dbg_parms[1],
- dbg_parms[2]);
+ KDBG_RELEASE(code, kdebug_vnode(vnp), path_words[0], path_words[1],
+ path_words[2]);
}
code &= ~DBG_FUNC_START;
- for (i = 3, dbg_namelen -= (3 * sizeof(long)); dbg_namelen > 0; i += 4, dbg_namelen -= (4 * sizeof(long))) {
- if (dbg_namelen <= (int)(4 * sizeof(long))) {
+ for (int i = 3; i * (int)sizeof(long) < path_len; i += 4) {
+ if ((i + 4) * (int)sizeof(long) >= path_len) {
code |= DBG_FUNC_END;
}
if (noprocfilt) {
- KDBG_RELEASE_NOPROCFILT(code, dbg_parms[i], dbg_parms[i + 1],
- dbg_parms[i + 2], dbg_parms[i + 3]);
+ KDBG_RELEASE_NOPROCFILT(code, path_words[i], path_words[i + 1],
+ path_words[i + 2], path_words[i + 3]);
} else {
- KDBG_RELEASE(code, dbg_parms[i], dbg_parms[i + 1], dbg_parms[i + 2],
- dbg_parms[i + 3]);
+ KDBG_RELEASE(code, path_words[i], path_words[i + 1],
+ path_words[i + 2], path_words[i + 3]);
}
}
}
void
-kdebug_lookup_gen_events(long *dbg_parms, int dbg_namelen, void *dp,
- bool lookup)
+kdebug_lookup_gen_events(long *path_words, int path_len, void *vnp, bool lookup)
{
- kdebug_vfs_lookup(dbg_parms, dbg_namelen, dp,
+ assert(path_len >= 0);
+ kdebug_vfs_lookup((unsigned long *)path_words, path_len, vnp,
lookup ? KDBG_VFS_LOOKUP_FLAG_LOOKUP : 0);
}
void
-kdebug_lookup(vnode_t dp, struct componentname *cnp)
+kdebug_lookup(vnode_t vnp, struct componentname *cnp)
{
- int dbg_namelen;
- char *dbg_nameptr;
- long dbg_parms[NUMPARMS];
-
- /* Collect the pathname for tracing */
- dbg_namelen = (cnp->cn_nameptr - cnp->cn_pnbuf) + cnp->cn_namelen;
- dbg_nameptr = cnp->cn_nameptr + cnp->cn_namelen;
+ unsigned long path_words[NUMPARMS];
- if (dbg_namelen > (int)sizeof(dbg_parms)) {
- dbg_namelen = sizeof(dbg_parms);
- }
- dbg_nameptr -= dbg_namelen;
+ /*
+ * Truncate the leading portion of the path to fit in path_words.
+ */
+ char *path_end = cnp->cn_nameptr + cnp->cn_namelen;
+ size_t path_len = MIN(path_end - cnp->cn_pnbuf,
+ (ssize_t)sizeof(path_words));
+ assert(path_len >= 0);
+ char *path_trunc = path_end - path_len;
- /* Copy the (possibly truncated) path itself */
- memcpy(dbg_parms, dbg_nameptr, dbg_namelen);
+ memcpy(path_words, path_trunc, path_len);
- /* Pad with '\0' or '>' */
- if (dbg_namelen < (int)sizeof(dbg_parms)) {
- memset((char *)dbg_parms + dbg_namelen,
- *(cnp->cn_nameptr + cnp->cn_namelen) ? '>' : 0,
- sizeof(dbg_parms) - dbg_namelen);
+ /*
+ * Pad with '\0' or '>'.
+ */
+ if (path_len < (ssize_t)sizeof(path_words)) {
+ bool complete_str = *(cnp->cn_nameptr + cnp->cn_namelen) == '\0';
+ memset((char *)path_words + path_len, complete_str ? '\0' : '>',
+ sizeof(path_words) - path_len);
}
- kdebug_vfs_lookup(dbg_parms, dbg_namelen, (void *)dp,
- KDBG_VFS_LOOKUP_FLAG_LOOKUP);
+ kdebug_vfs_lookup(path_words, (int)path_len, vnp, KDBG_VFS_LOOKUP_FLAG_LOOKUP);
}
#else /* (KDEBUG_LEVEL >= KDEBUG_LEVEL_IST) */
struct mount *mp = NULL;
char *str;
char ch;
- uint32_t id;
+ unsigned long id;
ino64_t ino;
int error;
int length;
}
ch = *str;
- mp = mount_lookupby_volfsid(id, 1);
+ if (id > INT_MAX) {
+ return ENOENT;
+ }
+ mp = mount_lookupby_volfsid((int)id, 1);
if (mp == NULL) {
return EINVAL; /* unexpected failure */
}
realpath[0] = '\0';
/* Get the absolute path to this vnode. */
- error = build_path(vp, realpath, bufsize, &length, 0, ctx);
+ error = build_path(vp, realpath, (int)bufsize, &length, 0, ctx);
vnode_put(vp);
if (error == 0 && *str != '\0') {
- int attempt = strlcat(realpath, str, MAXPATHLEN);
+ size_t attempt = strlcat(realpath, str, MAXPATHLEN);
if (attempt > MAXPATHLEN) {
error = ENAMETOOLONG;
}