#include <sys/kauth.h>
#include <sys/user.h>
#include <sys/paths.h>
+#include <os/overflow.h>
#if CONFIG_MACF
#include <security/mac_framework.h>
}
/*
- * This function builds the path to a filename in "buff". The
- * length of the buffer *INCLUDING* the trailing zero byte is
- * returned in outlen. NOTE: the length includes the trailing
- * zero byte and thus the length is one greater than what strlen
- * would return. This is important and lots of code elsewhere
- * in the kernel assumes this behavior.
+ * This function builds the path in "buff" from the supplied vnode.
+ * The length of the buffer *INCLUDING* the trailing zero byte is
+ * returned in outlen. NOTE: the length includes the trailing zero
+ * byte and thus the length is one greater than what strlen would
+ * return. This is important and lots of code elsewhere in the kernel
+ * assumes this behavior.
*
* This function can call vnop in file system if the parent vnode
* does not exist or when called for hardlinks via volfs path.
* cross over mount points during building the path.
*
* passed in vp must have a valid io_count reference
+ *
+ * If parent vnode is non-NULL it also must have an io count. This
+ * allows build_path_with_parent to be safely called for operations
+ * unlink, rmdir and rename that already have io counts on the target
+ * and the directory. In this way build_path_with_parent does not have
+ * to try and obtain an additional io count on the parent. Taking an
+ * io count ont the parent can lead to dead lock if a forced unmount
+ * occures at the right moment. For a fuller explaination on how this
+ * can occur see the comment for vn_getpath_with_parent.
+ *
*/
int
-build_path(vnode_t first_vp, char *buff, int buflen, int *outlen, int flags, vfs_context_t ctx)
+build_path_with_parent(vnode_t first_vp, vnode_t parent_vp, char *buff, int buflen, int *outlen, int flags, vfs_context_t ctx)
{
vnode_t vp, tvp;
vnode_t vp_with_iocount;
NAME_CACHE_UNLOCK();
- if (vp != first_vp && vp != vp_with_iocount) {
+ if (vp != first_vp && vp != parent_vp && vp != vp_with_iocount) {
if (vp_with_iocount) {
vnode_put(vp_with_iocount);
vp_with_iocount = NULLVP;
NAME_CACHE_UNLOCK();
- if (vp != first_vp && vp != vp_with_iocount) {
+ if (vp != first_vp && vp != parent_vp && vp != vp_with_iocount) {
if (vp_with_iocount) {
vnode_put(vp_with_iocount);
vp_with_iocount = NULLVP;
return (ret);
}
+int
+build_path(vnode_t first_vp, char *buff, int buflen, int *outlen, int flags, vfs_context_t ctx)
+{
+ return (build_path_with_parent(first_vp, NULL, buff, buflen, outlen, flags, ctx));
+}
/*
* return NULLVP if vp's parent doesn't
{
struct namecache *ncp;
vnode_t old_parentvp = NULLVP;
-#if NAMEDSTREAMS
int isstream = (vp->v_flag & VISNAMEDSTREAM);
int kusecountbumped = 0;
-#endif
kauth_cred_t tcred = NULL;
const char *vname = NULL;
const char *tname = NULL;
if (dvp && vnode_ref(dvp) != 0) {
dvp = NULLVP;
}
-#if NAMEDSTREAMS
/* Don't count a stream's parent ref during unmounts */
if (isstream && dvp && (dvp != vp) && (dvp != vp->v_parent) && (dvp->v_type == VREG)) {
vnode_lock_spin(dvp);
kusecountbumped = 1;
vnode_unlock(dvp);
}
-#endif
} else {
dvp = NULLVP;
}
tcred = vp->v_cred;
vp->v_cred = NOCRED;
vp->v_authorized_actions = 0;
+ vp->v_cred_timestamp = 0;
}
if ( (flags & VNODE_UPDATE_NAME) ) {
vname = vp->v_name;
kauth_cred_unref(&tcred);
}
if (dvp != NULLVP) {
-#if NAMEDSTREAMS
/* Back-out the ref we took if we lost a race for vp->v_parent. */
if (kusecountbumped) {
vnode_lock_spin(dvp);
--dvp->v_kusecount;
vnode_unlock(dvp);
}
-#endif
vnode_rele(dvp);
}
if (old_parentvp) {
struct uthread *ut;
-#if NAMEDSTREAMS
if (isstream) {
vnode_lock_spin(old_parentvp);
if ((old_parentvp->v_type != VDIR) && (old_parentvp->v_kusecount > 0))
--old_parentvp->v_kusecount;
vnode_unlock(old_parentvp);
}
-#endif
ut = get_bsdthread_info(current_thread());
/*
#if CONFIG_MACF
- /*
+ /*
* Name cache provides authorization caching (see below)
* that will short circuit MAC checks in lookup().
* We must perform MAC check here. On denial
}
}
#endif /* MAC */
- if (ttl_enabled && ((tv.tv_sec - dp->v_cred_timestamp) > dp->v_mount->mnt_authcache_ttl))
+ if (ttl_enabled &&
+ (dp->v_mount->mnt_authcache_ttl == 0 ||
+ ((tv.tv_sec - dp->v_cred_timestamp) > dp->v_mount->mnt_authcache_ttl))) {
break;
+ }
/*
* NAME_CACHE_LOCK holds these fields stable
* so we make an ugly check for root here. root is always
* allowed and breaking out of here only to find out that is
* authorized by virtue of being root is very very expensive.
+ * However, the check for not root is valid only for filesystems
+ * which use local authorization.
+ *
+ * XXX: Remove the check for root when we can reliably set
+ * KAUTH_VNODE_SEARCHBYANYONE as root.
*/
if ((dp->v_cred != ucred || !(dp->v_authorized_actions & KAUTH_VNODE_SEARCH)) &&
!(dp->v_authorized_actions & KAUTH_VNODE_SEARCHBYANYONE) &&
- !vfs_context_issuser(ctx))
+ (ttl_enabled || !vfs_context_issuser(ctx))) {
break;
+ }
/*
* indicate that we're allowed to traverse this directory...
* Force directory hardlinks to go to
* file system for ".." requests.
*/
- if (dp && (dp->v_flag & VISHARDLINK)) {
+ if ((dp->v_flag & VISHARDLINK)) {
break;
}
/*
ncpp = NCHHASH(dvp, cnp->cn_hash);
LIST_FOREACH(ncp, ncpp, nc_hash) {
if ((ncp->nc_dvp == dvp) && (ncp->nc_hashval == hashval)) {
- if (memcmp(ncp->nc_name, cnp->cn_nameptr, namelen) == 0 && ncp->nc_name[namelen] == 0)
+ if (strncmp(ncp->nc_name, cnp->cn_nameptr, namelen) == 0 && ncp->nc_name[namelen] == 0)
break;
}
}
ncpp = NCHHASH(dvp, cnp->cn_hash);
LIST_FOREACH(ncp, ncpp, nc_hash) {
if ((ncp->nc_dvp == dvp) && (ncp->nc_hashval == hashval)) {
- if (memcmp(ncp->nc_name, cnp->cn_nameptr, namelen) == 0 && ncp->nc_name[namelen] == 0)
+ if (strncmp(ncp->nc_name, cnp->cn_nameptr, namelen) == 0 && ncp->nc_name[namelen] == 0)
break;
}
}
int
-resize_namecache(u_int newsize)
+resize_namecache(int newsize)
{
struct nchashhead *new_table;
struct nchashhead *old_table;
struct nchashhead *old_head, *head;
struct namecache *entry, *next;
uint32_t i, hashval;
- int dNodes, dNegNodes;
+ int dNodes, dNegNodes, nelements;
u_long new_size, old_size;
+ if (newsize < 0)
+ return EINVAL;
+
dNegNodes = (newsize / 10);
dNodes = newsize + dNegNodes;
-
// we don't support shrinking yet
if (dNodes <= desiredNodes) {
- return 0;
+ return 0;
}
- new_table = hashinit(2 * dNodes, M_CACHE, &nchashmask);
+
+ if (os_mul_overflow(dNodes, 2, &nelements)) {
+ return EINVAL;
+ }
+
+ new_table = hashinit(nelements, M_CACHE, &nchashmask);
new_size = nchashmask + 1;
if (new_table == NULL) {
- return ENOMEM;
+ return ENOMEM;
}
NAME_CACHE_LOCK();
}
static void
-cache_delete(struct namecache *ncp, int age_entry)
+cache_delete(struct namecache *ncp, int free_entry)
{
NCHSTAT(ncs_deletes);
*/
ncp->nc_hash.le_prev = NULL;
- if (age_entry) {
- /*
- * make it the next one available
- * for cache_enter's use
- */
- TAILQ_REMOVE(&nchead, ncp, nc_entry);
- TAILQ_INSERT_HEAD(&nchead, ncp, nc_entry);
- }
vfs_removename(ncp->nc_name);
ncp->nc_name = NULL;
+ if (free_entry) {
+ TAILQ_REMOVE(&nchead, ncp, nc_entry);
+ FREE_ZONE(ncp, sizeof(*ncp), M_CACHE);
+ numcache--;
+ }
}
lck_mtx_lock_spin(&strcache_mtx_locks[lock_index]);
for (entry = head->lh_first; entry != NULL; chain_len++, entry = entry->hash_chain.le_next) {
- if (memcmp(entry->str, name, len) == 0 && entry->str[len] == 0) {
+ if (strncmp(entry->str, name, len) == 0 && entry->str[len] == 0) {
entry->refcount++;
break;
}