]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/posix_sem.c
xnu-4570.31.3.tar.gz
[apple/xnu.git] / bsd / kern / posix_sem.c
index 42a12cb7e794713d6116d30781b0b66c24dc5872..9dc882363251acc510b282af4cfd551f7804d441 100644 (file)
@@ -124,6 +124,10 @@ struct     psemcache {
 };
 #define PSEMCACHE_NULL (struct psemcache *)0
 
+#define PSEMCACHE_NOTFOUND (0)
+#define PSEMCACHE_FOUND    (-1)
+#define PSEMCACHE_NEGATIVE (ENOENT)
+
 struct psemstats {
        long    goodhits;               /* hits that we can really use */
        long    neghits;                /* negative hits that we can use */
@@ -164,29 +168,31 @@ struct psemstats psemstats;               /* cache effectiveness statistics */
 
 static int psem_access(struct pseminfo *pinfo, int mode, kauth_cred_t cred);
 static int psem_cache_search(struct pseminfo **,
-                               struct psemname *, struct psemcache **);
+               struct psemname *, struct psemcache **);
 static int psem_delete(struct pseminfo * pinfo);
 
 static int psem_read (struct fileproc *fp, struct uio *uio,
-                           int flags, vfs_context_t ctx);
+               int flags, vfs_context_t ctx);
 static int psem_write (struct fileproc *fp, struct uio *uio,
-                           int flags, vfs_context_t ctx);
+               int flags, vfs_context_t ctx);
 static int psem_ioctl (struct fileproc *fp, u_long com,
-                           caddr_t data, vfs_context_t ctx);
+               caddr_t data, vfs_context_t ctx);
 static int psem_select (struct fileproc *fp, int which, void *wql, vfs_context_t ctx);
 static int psem_closefile (struct fileglob *fp, vfs_context_t ctx);
+static int psem_unlink_internal(struct pseminfo *pinfo, struct psemcache *pcache);
 
-static int psem_kqfilter (struct fileproc *fp, struct knote *kn, vfs_context_t ctx);
+static int psem_kqfilter (struct fileproc *fp, struct knote *kn,
+               struct kevent_internal_s *kev, vfs_context_t ctx);
 
 static const struct fileops psemops = {
-       DTYPE_PSXSEM,
-       psem_read,
-       psem_write,
-       psem_ioctl,
-       psem_select,
-       psem_closefile,
-       psem_kqfilter,
-       NULL
+       .fo_type = DTYPE_PSXSEM,
+       .fo_read = psem_read,
+       .fo_write = psem_write,
+       .fo_ioctl = psem_ioctl,
+       .fo_select = psem_select,
+       .fo_close = psem_closefile,
+       .fo_kqfilter = psem_kqfilter,
+       .fo_drain = NULL,
 };
 
 static lck_grp_t       *psx_sem_subsys_lck_grp;
@@ -196,9 +202,14 @@ static lck_mtx_t        psx_sem_subsys_mutex;
 
 #define PSEM_SUBSYS_LOCK() lck_mtx_lock(& psx_sem_subsys_mutex)
 #define PSEM_SUBSYS_UNLOCK() lck_mtx_unlock(& psx_sem_subsys_mutex)
+#define PSEM_SUBSYS_ASSERT_HELD() LCK_MTX_ASSERT(&psx_sem_subsys_mutex, LCK_MTX_ASSERT_OWNED)
 
 
 static int psem_cache_add(struct pseminfo *psemp, struct psemname *pnp, struct psemcache *pcp);
+static void psem_cache_delete(struct psemcache *pcp);
+int psem_cache_purge_all(proc_t);
+
+
 /* Initialize the mutex governing access to the posix sem subsystem */
 __private_extern__ void
 psem_lock_init( void )
@@ -231,7 +242,7 @@ psem_cache_search(struct pseminfo **psemp, struct psemname *pnp,
 
        if (pnp->psem_namelen > PSEMNAMLEN) {
                psemstats.longnames++;
-               return (0);
+               return PSEMCACHE_NOTFOUND;
        }
 
        pcpp = PSEMHASH(pnp);
@@ -244,7 +255,7 @@ psem_cache_search(struct pseminfo **psemp, struct psemname *pnp,
 
        if (pcp == 0) {
                psemstats.miss++;
-               return (0);
+               return PSEMCACHE_NOTFOUND;
        }
 
        /* We found a "positive" match, return the vnode */
@@ -253,7 +264,7 @@ psem_cache_search(struct pseminfo **psemp, struct psemname *pnp,
                /* TOUCH(ncp); */
                *psemp = pcp->pseminfo;
                *pcache = pcp;
-               return (-1);
+               return PSEMCACHE_FOUND;
        }
 
        /*
@@ -261,7 +272,7 @@ psem_cache_search(struct pseminfo **psemp, struct psemname *pnp,
         * The nc_vpid field records whether this is a whiteout.
         */
        psemstats.neghits++;
-       return (ENOENT);
+       return PSEMCACHE_NEGATIVE;
 }
 
 /*
@@ -281,11 +292,11 @@ psem_cache_add(struct pseminfo *psemp, struct psemname *pnp, struct psemcache *p
 
 
        /*  if the entry has already been added by some one else return */
-       if (psem_cache_search(&dpinfo, pnp, &dpcp) == -1) {
-               return(EEXIST);
+       if (psem_cache_search(&dpinfo, pnp, &dpcp) == PSEMCACHE_FOUND) {
+               return EEXIST;
        }
        if (psemnument >= posix_sem_max)
-               return(ENOSPC);
+               return ENOSPC;
        psemnument++;
        /*
         * Fill in cache info, if vp is NULL this is a "negative" cache entry.
@@ -307,7 +318,7 @@ psem_cache_add(struct pseminfo *psemp, struct psemname *pnp, struct psemcache *p
        }
 #endif
        LIST_INSERT_HEAD(pcpp, pcp, psem_hash);
-       return(0);
+       return 0;
 }
 
 /*
@@ -333,27 +344,43 @@ psem_cache_delete(struct psemcache *pcp)
        psemnument--;
 }
 
-#if NOT_USED
 /*
- * Invalidate a all entries to particular vnode.
- * 
- * We actually just increment the v_id, that will do it. The entries will
- * be purged by lookup as they get found. If the v_id wraps around, we
- * need to ditch the entire cache, to avoid confusion. No valid vnode will
- * ever have (v_id == 0).
+ * Remove all cached psem entries. Open semaphores (with a positive refcount)
+ * will continue to exist, but their cache entries tying them to a particular
+ * name/path will be removed making all future lookups on the name fail.
  */
-static void
-psem_cache_purge(void)
+int
+psem_cache_purge_all(__unused proc_t p)
 {
-       struct psemcache *pcp;
+       struct psemcache *pcp, *tmppcp;
        struct psemhashhead *pcpp;
+       int error = 0;
+
+       if (kauth_cred_issuser(kauth_cred_get()) == 0)
+               return EPERM;
 
+       PSEM_SUBSYS_LOCK();
        for (pcpp = &psemhashtbl[psemhash]; pcpp >= psemhashtbl; pcpp--) {
-               while ( (pcp = pcpp->lh_first) )
-                       psem_cache_delete(pcp);
+               LIST_FOREACH_SAFE(pcp, pcpp, psem_hash, tmppcp) {
+                       assert(pcp->psem_nlen);
+                       /*
+                        * unconditionally unlink the cache entry
+                        */
+                       error = psem_unlink_internal(pcp->pseminfo, pcp);
+                       if (error)
+                               goto out;
+               }
        }
+       assert(psemnument == 0);
+
+out:
+       PSEM_SUBSYS_UNLOCK();
+
+       if (error)
+               printf("%s: Error %d removing all semaphores: %ld remain!\n",
+                      __func__, error, psemnument);
+       return error;
 }
-#endif /* NOT_USED */
 
 int
 sem_open(proc_t p, struct sem_open_args *uap, user_addr_t *retval)
@@ -387,7 +414,7 @@ sem_open(proc_t p, struct sem_open_args *uap, user_addr_t *retval)
         * Preallocate everything we might need up front to avoid taking
         * and dropping the lock, opening us up to race conditions.
         */
-       MALLOC_ZONE(pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
+       MALLOC_ZONE(pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK | M_ZERO);
        if (pnbuf == NULL) {
                error = ENOSPC;
                goto bad;
@@ -498,15 +525,15 @@ sem_open(proc_t p, struct sem_open_args *uap, user_addr_t *retval)
        PSEM_SUBSYS_LOCK();
        error = psem_cache_search(&pinfo, &nd, &pcache);
 
-       if (error == ENOENT) {
+       if (error == PSEMCACHE_NEGATIVE) {
                error = EINVAL;
                goto bad_locked;
-
        }
-       if (!error) {
-               incache = 0;
-       } else
+
+       if (error == PSEMCACHE_FOUND)
                incache = 1;
+       else
+               incache = 0;
 
        cmode &=  ALLPERMS;
 
@@ -661,6 +688,39 @@ psem_access(struct pseminfo *pinfo, int mode, kauth_cred_t cred)
        return(posix_cred_access(cred, pinfo->psem_uid, pinfo->psem_gid, pinfo->psem_mode, mode_req));
 }
 
+static int
+psem_unlink_internal(struct pseminfo *pinfo, struct psemcache *pcache)
+{
+       PSEM_SUBSYS_ASSERT_HELD();
+
+       if (!pinfo || !pcache)
+               return EINVAL;
+
+       if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED)) == 0)
+               return EINVAL;
+
+       if (pinfo->psem_flags & PSEM_INDELETE)
+               return 0;
+
+       AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid, pinfo->psem_gid,
+                 pinfo->psem_mode);
+
+       pinfo->psem_flags |= PSEM_INDELETE;
+       pinfo->psem_usecount--;
+
+       if (!pinfo->psem_usecount) {
+               psem_delete(pinfo);
+               FREE(pinfo,M_SHM);
+       } else {
+               pinfo->psem_flags |= PSEM_REMOVED;
+       }
+
+       psem_cache_delete(pcache);
+       FREE(pcache, M_SHM);
+       return 0;
+}
+
+
 int
 sem_unlink(__unused proc_t p, struct sem_unlink_args *uap, __unused int32_t *retval)
 {
@@ -668,11 +728,10 @@ sem_unlink(__unused proc_t p, struct sem_unlink_args *uap, __unused int32_t *ret
        int error=0;
        struct psemname nd;
        struct pseminfo *pinfo;
-       char * pnbuf;
        char * nameptr;
        char * cp;
-       size_t pathlen, plen;
-       int incache = 0;
+       char * pnbuf;
+       size_t pathlen;
        struct psemcache *pcache = PSEMCACHE_NULL;
 
        pinfo = PSEMINFO_NULL;
@@ -692,12 +751,12 @@ sem_unlink(__unused proc_t p, struct sem_unlink_args *uap, __unused int32_t *ret
                goto bad;
        }
 
+       nameptr = pnbuf;
 
 #ifdef PSXSEM_NAME_RESTRICT
-       nameptr = pnbuf;
        if (*nameptr == '/') {
                while (*(nameptr++) == '/') {
-                       plen--;
+                       pathlen--;
                        error = EINVAL;
                        goto bad;
                }
@@ -707,31 +766,24 @@ sem_unlink(__unused proc_t p, struct sem_unlink_args *uap, __unused int32_t *ret
        }
 #endif /* PSXSEM_NAME_RESTRICT */
 
-       plen = pathlen;
-       nameptr = pnbuf;
        nd.psem_nameptr = nameptr;
-       nd.psem_namelen = plen;
+       nd.psem_namelen = pathlen;
        nd. psem_hash =0;
 
-        for (cp = nameptr, i=1; *cp != 0 && i <= plen; i++, cp++) {
+        for (cp = nameptr, i=1; *cp != 0 && i <= pathlen; i++, cp++) {
                nd.psem_hash += (unsigned char)*cp * i;
        }
 
        PSEM_SUBSYS_LOCK();
        error = psem_cache_search(&pinfo, &nd, &pcache);
 
-       if (error == ENOENT) {
+       if (error != PSEMCACHE_FOUND) {
                PSEM_SUBSYS_UNLOCK();
                error = EINVAL;
                goto bad;
 
        }
-       if (!error) {
-               PSEM_SUBSYS_UNLOCK();
-               error = EINVAL;
-               goto bad;
-       } else
-               incache = 1;
+
 #if CONFIG_MACF
        error = mac_posixsem_check_unlink(kauth_cred_get(), pinfo, nameptr);
        if (error) {
@@ -744,37 +796,12 @@ sem_unlink(__unused proc_t p, struct sem_unlink_args *uap, __unused int32_t *ret
                goto bad;
        }
 
-       if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED))==0) {
-               PSEM_SUBSYS_UNLOCK();
-               error = EINVAL;
-               goto bad;
-       }
-
-       if ( (pinfo->psem_flags & PSEM_INDELETE) ) {
-               PSEM_SUBSYS_UNLOCK();
-               error = 0;
-               goto bad;
-       }
-
-       AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid, pinfo->psem_gid,
-                 pinfo->psem_mode);
-
-       pinfo->psem_flags |= PSEM_INDELETE;
-       pinfo->psem_usecount--;
-
-       if (!pinfo->psem_usecount) {
-               psem_delete(pinfo);
-               FREE(pinfo,M_SHM);
-       } else
-               pinfo->psem_flags |= PSEM_REMOVED;
-
-       psem_cache_delete(pcache);
+       error = psem_unlink_internal(pinfo, pcache);
        PSEM_SUBSYS_UNLOCK();
-       FREE(pcache, M_SHM);
-       error = 0;
+
 bad:
        FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI);
-       return (error);
+       return error;
 }
 
 int
@@ -1067,38 +1094,40 @@ psem_delete(struct pseminfo * pinfo)
 }
 
 static int
-psem_read(__unused struct fileproc *fp, __unused struct uio *uio, 
-                 __unused int flags, __unused vfs_context_t ctx)
+psem_read(__unused struct fileproc *fp, __unused struct uio *uio,
+               __unused int flags, __unused vfs_context_t ctx)
 {
        return(ENOTSUP);
 }
 
 static int
-psem_write(__unused struct fileproc *fp, __unused struct uio *uio, 
-                  __unused int flags, __unused vfs_context_t ctx)
+psem_write(__unused struct fileproc *fp, __unused struct uio *uio,
+               __unused int flags, __unused vfs_context_t ctx)
 {
        return(ENOTSUP);
 }
 
 static int
-psem_ioctl(__unused struct fileproc *fp, __unused u_long com, 
-                       __unused caddr_t data, __unused vfs_context_t ctx)
+psem_ioctl(__unused struct fileproc *fp, __unused u_long com,
+               __unused caddr_t data, __unused vfs_context_t ctx)
 {
        return(ENOTSUP);
 }
 
 static int
-psem_select(__unused struct fileproc *fp, __unused int which, 
-                       __unused void *wql, __unused vfs_context_t ctx)
+psem_select(__unused struct fileproc *fp, __unused int which,
+               __unused void *wql, __unused vfs_context_t ctx)
 {
        return(ENOTSUP);
 }
 
 static int
-psem_kqfilter(__unused struct fileproc *fp, __unused struct knote *kn, 
-                               __unused vfs_context_t ctx)
+psem_kqfilter(__unused struct fileproc *fp, struct knote *kn,
+               __unused struct kevent_internal_s *kev, __unused vfs_context_t ctx)
 {
-       return (ENOTSUP);
+       kn->kn_flags = EV_ERROR;
+       kn->kn_data = ENOTSUP;
+       return 0;
 }
 
 int