]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
e5568f75 | 2 | * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. |
1c79356b A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
37839358 A |
6 | * The contents of this file constitute Original Code as defined in and |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
1c79356b | 11 | * |
37839358 A |
12 | * This Original Code and all software distributed under the License are |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
1c79356b A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
37839358 A |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
1c79356b A |
19 | * |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* | |
23 | * Copyright (c) 1990, 1996-1998 Apple Computer, Inc. | |
24 | * All Rights Reserved. | |
25 | */ | |
26 | /* | |
9bccf70c | 27 | * posix_shm.c : Support for POSIX semaphore APIs |
1c79356b A |
28 | * |
29 | * File: posix_sem.c | |
30 | * Author: Ananthakrishna Ramesh | |
31 | * | |
32 | * HISTORY | |
33 | * 2-Sep-1999 A.Ramesh | |
34 | * Created for MacOSX | |
35 | * | |
36 | */ | |
37 | ||
38 | #include <sys/cdefs.h> | |
39 | #include <sys/param.h> | |
40 | #include <sys/systm.h> | |
41 | #include <sys/kernel.h> | |
91447636 | 42 | #include <sys/file_internal.h> |
1c79356b A |
43 | #include <sys/filedesc.h> |
44 | #include <sys/stat.h> | |
91447636 A |
45 | #include <sys/proc_internal.h> |
46 | #include <sys/kauth.h> | |
1c79356b A |
47 | #include <sys/mount.h> |
48 | #include <sys/namei.h> | |
49 | #include <sys/vnode.h> | |
50 | #include <sys/ioctl.h> | |
51 | #include <sys/tty.h> | |
52 | #include <sys/malloc.h> | |
53 | #include <sys/semaphore.h> | |
91447636 | 54 | #include <sys/sysproto.h> |
e5568f75 A |
55 | |
56 | #include <bsm/audit_kernel.h> | |
57 | ||
1c79356b A |
58 | #include <mach/mach_types.h> |
59 | #include <mach/vm_prot.h> | |
60 | #include <mach/semaphore.h> | |
61 | #include <mach/sync_policy.h> | |
91447636 A |
62 | #include <mach/task.h> |
63 | #include <kern/kern_types.h> | |
1c79356b A |
64 | #include <kern/task.h> |
65 | #include <kern/clock.h> | |
66 | #include <mach/kern_return.h> | |
67 | ||
91447636 A |
68 | #if KTRACE |
69 | #include <sys/ktrace.h> | |
70 | #endif | |
71 | ||
72 | #define f_flag f_fglob->fg_flag | |
73 | #define f_type f_fglob->fg_type | |
74 | #define f_msgcount f_fglob->fg_msgcount | |
75 | #define f_cred f_fglob->fg_cred | |
76 | #define f_ops f_fglob->fg_ops | |
77 | #define f_offset f_fglob->fg_offset | |
78 | #define f_data f_fglob->fg_data | |
1c79356b A |
79 | #define PSEMNAMLEN 31 /* maximum name segment length we bother with */ |
80 | ||
81 | struct pseminfo { | |
82 | unsigned int psem_flags; | |
83 | unsigned int psem_usecount; | |
84 | mode_t psem_mode; | |
85 | uid_t psem_uid; | |
86 | gid_t psem_gid; | |
87 | char psem_name[PSEMNAMLEN + 1]; /* segment name */ | |
91447636 | 88 | semaphore_t psem_semobject; |
1c79356b A |
89 | struct proc * sem_proc; |
90 | }; | |
91 | #define PSEMINFO_NULL (struct pseminfo *)0 | |
92 | ||
93 | #define PSEM_NONE 1 | |
94 | #define PSEM_DEFINED 2 | |
95 | #define PSEM_ALLOCATED 4 | |
96 | #define PSEM_MAPPED 8 | |
97 | #define PSEM_INUSE 0x10 | |
98 | #define PSEM_REMOVED 0x20 | |
99 | #define PSEM_INCREATE 0x40 | |
100 | #define PSEM_INDELETE 0x80 | |
101 | ||
102 | struct psemcache { | |
103 | LIST_ENTRY(psemcache) psem_hash; /* hash chain */ | |
104 | struct pseminfo *pseminfo; /* vnode the name refers to */ | |
105 | int psem_nlen; /* length of name */ | |
106 | char psem_name[PSEMNAMLEN + 1]; /* segment name */ | |
107 | }; | |
108 | #define PSEMCACHE_NULL (struct psemcache *)0 | |
109 | ||
110 | struct psemstats { | |
111 | long goodhits; /* hits that we can really use */ | |
112 | long neghits; /* negative hits that we can use */ | |
113 | long badhits; /* hits we must drop */ | |
114 | long falsehits; /* hits with id mismatch */ | |
115 | long miss; /* misses */ | |
116 | long longnames; /* long names that ignore cache */ | |
117 | }; | |
118 | ||
119 | struct psemname { | |
120 | char *psem_nameptr; /* pointer to looked up name */ | |
121 | long psem_namelen; /* length of looked up component */ | |
122 | u_long psem_hash; /* hash value of looked up name */ | |
123 | }; | |
124 | ||
125 | struct psemnode { | |
126 | struct pseminfo *pinfo; | |
127 | #if DIAGNOSTIC | |
128 | unsigned int readcnt; | |
129 | unsigned int writecnt; | |
130 | #endif | |
131 | }; | |
132 | #define PSEMNODE_NULL (struct psemnode *)0 | |
133 | ||
134 | ||
135 | #define PSEMHASH(pnp) \ | |
136 | (&psemhashtbl[(pnp)->psem_hash & psemhash]) | |
137 | LIST_HEAD(psemhashhead, psemcache) *psemhashtbl; /* Hash Table */ | |
138 | u_long psemhash; /* size of hash table - 1 */ | |
139 | long psemnument; /* number of cache entries allocated */ | |
91447636 A |
140 | long posix_sem_max = 10000; /* tunable for max POSIX semaphores */ |
141 | /* 10000 limits to ~1M of memory */ | |
142 | SYSCTL_NODE(_kern, KERN_POSIX, posix, CTLFLAG_RW, 0, "Posix"); | |
143 | SYSCTL_NODE(_kern_posix, OID_AUTO, sem, CTLFLAG_RW, 0, "Semaphores"); | |
144 | SYSCTL_INT (_kern_posix_sem, OID_AUTO, max, CTLFLAG_RW, &posix_sem_max, 0, "max"); | |
145 | ||
1c79356b A |
146 | struct psemstats psemstats; /* cache effectiveness statistics */ |
147 | ||
91447636 A |
148 | static int psem_access(struct pseminfo *pinfo, int mode, kauth_cred_t cred); |
149 | static int psem_cache_search(struct pseminfo **, | |
150 | struct psemname *, struct psemcache **); | |
151 | static int psem_delete(struct pseminfo * pinfo); | |
1c79356b | 152 | |
91447636 A |
153 | static int psem_read (struct fileproc *fp, struct uio *uio, |
154 | kauth_cred_t cred, int flags, struct proc *p); | |
155 | static int psem_write (struct fileproc *fp, struct uio *uio, | |
156 | kauth_cred_t cred, int flags, struct proc *p); | |
157 | static int psem_ioctl (struct fileproc *fp, u_long com, | |
158 | caddr_t data, struct proc *p); | |
159 | static int psem_select (struct fileproc *fp, int which, void *wql, struct proc *p); | |
160 | static int psem_closefile (struct fileglob *fp, struct proc *p); | |
1c79356b | 161 | |
91447636 | 162 | static int psem_kqfilter (struct fileproc *fp, struct knote *kn, struct proc *p); |
55e303ae | 163 | |
1c79356b | 164 | struct fileops psemops = |
91447636 A |
165 | { psem_read, psem_write, psem_ioctl, psem_select, psem_closefile, psem_kqfilter, 0 }; |
166 | ||
167 | ||
168 | static lck_grp_t *psx_sem_subsys_lck_grp; | |
169 | static lck_grp_attr_t *psx_sem_subsys_lck_grp_attr; | |
170 | static lck_attr_t *psx_sem_subsys_lck_attr; | |
171 | static lck_mtx_t psx_sem_subsys_mutex; | |
172 | ||
173 | #define PSEM_SUBSYS_LOCK() lck_mtx_lock(& psx_sem_subsys_mutex) | |
174 | #define PSEM_SUBSYS_UNLOCK() lck_mtx_unlock(& psx_sem_subsys_mutex) | |
175 | ||
176 | ||
177 | static int psem_cache_add(struct pseminfo *psemp, struct psemname *pnp, struct psemcache *pcp); | |
178 | /* Initialize the mutex governing access to the posix sem subsystem */ | |
179 | __private_extern__ void | |
180 | psem_lock_init( void ) | |
181 | { | |
182 | ||
183 | psx_sem_subsys_lck_grp_attr = lck_grp_attr_alloc_init(); | |
184 | lck_grp_attr_setstat(psx_sem_subsys_lck_grp_attr); | |
185 | ||
186 | psx_sem_subsys_lck_grp = lck_grp_alloc_init("posix shared memory", psx_sem_subsys_lck_grp_attr); | |
187 | ||
188 | psx_sem_subsys_lck_attr = lck_attr_alloc_init(); | |
189 | /* lck_attr_setdebug(psx_sem_subsys_lck_attr); */ | |
190 | lck_mtx_init(& psx_sem_subsys_mutex, psx_sem_subsys_lck_grp, psx_sem_subsys_lck_attr); | |
191 | } | |
1c79356b A |
192 | |
193 | /* | |
194 | * Lookup an entry in the cache | |
195 | * | |
196 | * | |
197 | * status of -1 is returned if matches | |
198 | * If the lookup determines that the name does not exist | |
199 | * (negative cacheing), a status of ENOENT is returned. If the lookup | |
200 | * fails, a status of zero is returned. | |
201 | */ | |
202 | ||
9bccf70c | 203 | static int |
1c79356b A |
204 | psem_cache_search(psemp, pnp, pcache) |
205 | struct pseminfo **psemp; | |
206 | struct psemname *pnp; | |
207 | struct psemcache **pcache; | |
208 | { | |
91447636 A |
209 | struct psemcache *pcp, *nnp; |
210 | struct psemhashhead *pcpp; | |
1c79356b A |
211 | |
212 | if (pnp->psem_namelen > PSEMNAMLEN) { | |
213 | psemstats.longnames++; | |
214 | return (0); | |
215 | } | |
216 | ||
217 | pcpp = PSEMHASH(pnp); | |
218 | for (pcp = pcpp->lh_first; pcp != 0; pcp = nnp) { | |
219 | nnp = pcp->psem_hash.le_next; | |
220 | if (pcp->psem_nlen == pnp->psem_namelen && | |
221 | !bcmp(pcp->psem_name, pnp->psem_nameptr, (u_int)pcp-> psem_nlen)) | |
222 | break; | |
223 | } | |
224 | ||
225 | if (pcp == 0) { | |
226 | psemstats.miss++; | |
227 | return (0); | |
228 | } | |
229 | ||
230 | /* We found a "positive" match, return the vnode */ | |
231 | if (pcp->pseminfo) { | |
232 | psemstats.goodhits++; | |
233 | /* TOUCH(ncp); */ | |
234 | *psemp = pcp->pseminfo; | |
235 | *pcache = pcp; | |
236 | return (-1); | |
237 | } | |
238 | ||
239 | /* | |
240 | * We found a "negative" match, ENOENT notifies client of this match. | |
241 | * The nc_vpid field records whether this is a whiteout. | |
242 | */ | |
243 | psemstats.neghits++; | |
244 | return (ENOENT); | |
245 | } | |
246 | ||
247 | /* | |
248 | * Add an entry to the cache. | |
249 | */ | |
9bccf70c | 250 | static int |
91447636 | 251 | psem_cache_add(struct pseminfo *psemp, struct psemname *pnp, struct psemcache *pcp) |
1c79356b | 252 | { |
91447636 | 253 | struct psemhashhead *pcpp; |
1c79356b A |
254 | struct pseminfo *dpinfo; |
255 | struct psemcache *dpcp; | |
256 | ||
257 | #if DIAGNOSTIC | |
258 | if (pnp->psem_namelen > NCHNAMLEN) | |
259 | panic("cache_enter: name too long"); | |
260 | #endif | |
261 | ||
91447636 | 262 | |
1c79356b A |
263 | /* if the entry has already been added by some one else return */ |
264 | if (psem_cache_search(&dpinfo, pnp, &dpcp) == -1) { | |
1c79356b A |
265 | return(EEXIST); |
266 | } | |
91447636 A |
267 | if (psemnument >= posix_sem_max) |
268 | return(ENOSPC); | |
1c79356b | 269 | psemnument++; |
1c79356b A |
270 | /* |
271 | * Fill in cache info, if vp is NULL this is a "negative" cache entry. | |
272 | * For negative entries, we have to record whether it is a whiteout. | |
273 | * the whiteout flag is stored in the nc_vpid field which is | |
274 | * otherwise unused. | |
275 | */ | |
276 | pcp->pseminfo = psemp; | |
277 | pcp->psem_nlen = pnp->psem_namelen; | |
278 | bcopy(pnp->psem_nameptr, pcp->psem_name, (unsigned)pcp->psem_nlen); | |
279 | pcpp = PSEMHASH(pnp); | |
280 | #if DIAGNOSTIC | |
281 | { | |
91447636 | 282 | struct psemcache *p; |
1c79356b A |
283 | |
284 | for (p = pcpp->lh_first; p != 0; p = p->psem_hash.le_next) | |
285 | if (p == pcp) | |
286 | panic("psem:cache_enter duplicate"); | |
287 | } | |
288 | #endif | |
289 | LIST_INSERT_HEAD(pcpp, pcp, psem_hash); | |
290 | return(0); | |
291 | } | |
292 | ||
293 | /* | |
294 | * Name cache initialization, from vfs_init() when we are booting | |
295 | */ | |
296 | void | |
91447636 | 297 | psem_cache_init(void) |
1c79356b A |
298 | { |
299 | psemhashtbl = hashinit(desiredvnodes, M_SHM, &psemhash); | |
300 | } | |
301 | ||
9bccf70c | 302 | static void |
91447636 | 303 | psem_cache_delete(struct psemcache *pcp) |
9bccf70c A |
304 | { |
305 | #if DIAGNOSTIC | |
306 | if (pcp->psem_hash.le_prev == 0) | |
307 | panic("psem namecache purge le_prev"); | |
308 | if (pcp->psem_hash.le_next == pcp) | |
309 | panic("namecache purge le_next"); | |
310 | #endif /* DIAGNOSTIC */ | |
311 | LIST_REMOVE(pcp, psem_hash); | |
312 | pcp->psem_hash.le_prev = 0; | |
313 | psemnument--; | |
314 | } | |
315 | ||
91447636 | 316 | #if NOT_USED |
1c79356b A |
317 | /* |
318 | * Invalidate a all entries to particular vnode. | |
319 | * | |
320 | * We actually just increment the v_id, that will do it. The entries will | |
321 | * be purged by lookup as they get found. If the v_id wraps around, we | |
322 | * need to ditch the entire cache, to avoid confusion. No valid vnode will | |
323 | * ever have (v_id == 0). | |
324 | */ | |
91447636 | 325 | static void |
1c79356b A |
326 | psem_cache_purge(void) |
327 | { | |
328 | struct psemcache *pcp; | |
329 | struct psemhashhead *pcpp; | |
330 | ||
331 | for (pcpp = &psemhashtbl[psemhash]; pcpp >= psemhashtbl; pcpp--) { | |
91447636 | 332 | while ( (pcp = pcpp->lh_first) ) |
1c79356b A |
333 | psem_cache_delete(pcp); |
334 | } | |
335 | } | |
91447636 | 336 | #endif /* NOT_USED */ |
1c79356b A |
337 | |
338 | int | |
91447636 | 339 | sem_open(struct proc *p, struct sem_open_args *uap, user_addr_t *retval) |
1c79356b | 340 | { |
91447636 A |
341 | struct fileproc *fp; |
342 | size_t i; | |
343 | struct fileproc *nfp; | |
344 | int indx, error; | |
1c79356b A |
345 | struct psemname nd; |
346 | struct pseminfo *pinfo; | |
91447636 | 347 | struct psemcache *pcp; |
1c79356b A |
348 | char * pnbuf; |
349 | char * nameptr; | |
350 | char * cp; | |
351 | size_t pathlen, plen; | |
352 | int fmode ; | |
353 | int cmode = uap->mode; | |
354 | int value = uap->value; | |
355 | int incache = 0; | |
356 | struct psemnode * pnode = PSEMNODE_NULL; | |
357 | struct psemcache * pcache = PSEMCACHE_NULL; | |
358 | kern_return_t kret = KERN_SUCCESS; | |
359 | int pinfo_alloc = 0; | |
360 | ||
e5568f75 A |
361 | AUDIT_ARG(fflags, uap->oflag); |
362 | AUDIT_ARG(mode, uap->mode); | |
363 | AUDIT_ARG(value, uap->value); | |
91447636 | 364 | |
1c79356b A |
365 | pinfo = PSEMINFO_NULL; |
366 | ||
91447636 A |
367 | MALLOC_ZONE(pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); |
368 | if (pnbuf == NULL) | |
369 | return(ENOSPC); | |
370 | ||
1c79356b | 371 | pathlen = MAXPATHLEN; |
91447636 | 372 | error = copyinstr(uap->name, pnbuf, MAXPATHLEN, &pathlen); |
1c79356b A |
373 | if (error) { |
374 | goto bad; | |
375 | } | |
e5568f75 | 376 | AUDIT_ARG(text, pnbuf); |
91447636 | 377 | if ( (pathlen > PSEMNAMLEN) ) { |
1c79356b A |
378 | error = ENAMETOOLONG; |
379 | goto bad; | |
380 | } | |
381 | ||
1c79356b A |
382 | #ifdef PSXSEM_NAME_RESTRICT |
383 | nameptr = pnbuf; | |
384 | if (*nameptr == '/') { | |
385 | while (*(nameptr++) == '/') { | |
386 | plen--; | |
387 | error = EINVAL; | |
388 | goto bad; | |
389 | } | |
390 | } else { | |
391 | error = EINVAL; | |
392 | goto bad; | |
393 | } | |
394 | #endif /* PSXSEM_NAME_RESTRICT */ | |
395 | ||
396 | plen = pathlen; | |
397 | nameptr = pnbuf; | |
398 | nd.psem_nameptr = nameptr; | |
399 | nd.psem_namelen = plen; | |
400 | nd. psem_hash =0; | |
401 | ||
402 | for (cp = nameptr, i=1; *cp != 0 && i <= plen; i++, cp++) { | |
403 | nd.psem_hash += (unsigned char)*cp * i; | |
404 | } | |
405 | ||
91447636 A |
406 | #if KTRACE |
407 | if (KTRPOINT(p, KTR_NAMEI)) | |
408 | ktrnamei(p->p_tracep, nameptr); | |
409 | #endif | |
410 | ||
411 | PSEM_SUBSYS_LOCK(); | |
1c79356b A |
412 | error = psem_cache_search(&pinfo, &nd, &pcache); |
413 | ||
414 | if (error == ENOENT) { | |
91447636 | 415 | PSEM_SUBSYS_UNLOCK(); |
1c79356b A |
416 | error = EINVAL; |
417 | goto bad; | |
418 | ||
419 | } | |
420 | if (!error) { | |
421 | incache = 0; | |
422 | } else | |
423 | incache = 1; | |
424 | fmode = FFLAGS(uap->oflag); | |
425 | ||
91447636 A |
426 | PSEM_SUBSYS_UNLOCK(); |
427 | error = falloc(p, &nfp, &indx); | |
428 | if (error) | |
1c79356b | 429 | goto bad; |
1c79356b | 430 | |
91447636 | 431 | PSEM_SUBSYS_LOCK(); |
1c79356b A |
432 | fp = nfp; |
433 | cmode &= ALLPERMS; | |
434 | ||
435 | if (((fmode & (O_CREAT | O_EXCL))==(O_CREAT | O_EXCL)) && incache) { | |
436 | /* sem exists and opened O_EXCL */ | |
437 | #if notyet | |
438 | if (pinfo->psem_flags & PSEM_INDELETE) { | |
439 | } | |
440 | #endif | |
e5568f75 | 441 | AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid, |
91447636 A |
442 | pinfo->psem_gid, pinfo->psem_mode); |
443 | PSEM_SUBSYS_UNLOCK(); | |
1c79356b A |
444 | error = EEXIST; |
445 | goto bad1; | |
446 | } | |
447 | if (((fmode & (O_CREAT | O_EXCL))== O_CREAT) && incache) { | |
448 | /* As per POSIX, O_CREAT has no effect */ | |
449 | fmode &= ~O_CREAT; | |
450 | } | |
451 | ||
91447636 | 452 | if ( (fmode & O_CREAT) ) { |
1c79356b | 453 | if((value < 0) && (value > SEM_VALUE_MAX)) { |
91447636 | 454 | PSEM_SUBSYS_UNLOCK(); |
1c79356b A |
455 | error = EINVAL; |
456 | goto bad1; | |
457 | } | |
91447636 A |
458 | PSEM_SUBSYS_UNLOCK(); |
459 | MALLOC(pinfo, struct pseminfo *, sizeof(struct pseminfo), M_SHM, M_WAITOK|M_ZERO); | |
460 | if (pinfo == NULL) { | |
461 | error = ENOSPC; | |
462 | goto bad1; | |
463 | } | |
464 | PSEM_SUBSYS_LOCK(); | |
465 | ||
1c79356b A |
466 | pinfo_alloc = 1; |
467 | pinfo->psem_flags = PSEM_DEFINED | PSEM_INCREATE; | |
468 | pinfo->psem_usecount = 1; | |
469 | pinfo->psem_mode = cmode; | |
91447636 A |
470 | pinfo->psem_uid = kauth_cred_getuid(kauth_cred_get()); |
471 | pinfo->psem_gid = kauth_cred_get()->cr_gid; | |
472 | PSEM_SUBSYS_UNLOCK(); | |
1c79356b A |
473 | kret = semaphore_create(kernel_task, &pinfo->psem_semobject, |
474 | SYNC_POLICY_FIFO, value); | |
475 | if(kret != KERN_SUCCESS) | |
476 | goto bad3; | |
91447636 | 477 | PSEM_SUBSYS_LOCK(); |
1c79356b A |
478 | pinfo->psem_flags &= ~PSEM_DEFINED; |
479 | pinfo->psem_flags |= PSEM_ALLOCATED; | |
480 | pinfo->sem_proc = p; | |
481 | } else { | |
482 | /* semaphore should exist as it is without O_CREAT */ | |
483 | if (!incache) { | |
91447636 | 484 | PSEM_SUBSYS_UNLOCK(); |
1c79356b A |
485 | error = ENOENT; |
486 | goto bad1; | |
487 | } | |
488 | if( pinfo->psem_flags & PSEM_INDELETE) { | |
91447636 | 489 | PSEM_SUBSYS_UNLOCK(); |
1c79356b A |
490 | error = ENOENT; |
491 | goto bad1; | |
492 | } | |
e5568f75 | 493 | AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid, |
91447636 A |
494 | pinfo->psem_gid, pinfo->psem_mode); |
495 | if ( (error = psem_access(pinfo, fmode, kauth_cred_get())) ) { | |
496 | PSEM_SUBSYS_UNLOCK(); | |
1c79356b | 497 | goto bad1; |
91447636 | 498 | } |
1c79356b | 499 | } |
91447636 A |
500 | PSEM_SUBSYS_UNLOCK(); |
501 | MALLOC(pnode, struct psemnode *, sizeof(struct psemnode), M_SHM, M_WAITOK|M_ZERO); | |
502 | if (pnode == NULL) { | |
503 | error = ENOSPC; | |
504 | goto bad1; | |
505 | } | |
506 | if (!incache) { | |
507 | /* | |
508 | * We allocate a new entry if we are less than the maximum | |
509 | * allowed and the one at the front of the LRU list is in use. | |
510 | * Otherwise we use the one at the front of the LRU list. | |
511 | */ | |
512 | MALLOC(pcp, struct psemcache *, sizeof(struct psemcache), M_SHM, M_WAITOK|M_ZERO); | |
513 | if (pcp == NULL) { | |
514 | error = ENOMEM; | |
515 | goto bad2; | |
516 | } | |
1c79356b | 517 | |
91447636 A |
518 | } |
519 | PSEM_SUBSYS_LOCK(); | |
1c79356b | 520 | if (!incache) { |
91447636 A |
521 | if ( (error = psem_cache_add(pinfo, &nd, pcp)) ) { |
522 | PSEM_SUBSYS_UNLOCK(); | |
523 | FREE(pcp, M_SHM); | |
524 | goto bad2; | |
1c79356b A |
525 | } |
526 | } | |
527 | pinfo->psem_flags &= ~PSEM_INCREATE; | |
528 | pinfo->psem_usecount++; | |
529 | pnode->pinfo = pinfo; | |
91447636 A |
530 | PSEM_SUBSYS_UNLOCK(); |
531 | ||
532 | proc_fdlock(p); | |
55e303ae | 533 | fp->f_flag = fmode & FMASK; |
1c79356b A |
534 | fp->f_type = DTYPE_PSXSEM; |
535 | fp->f_ops = &psemops; | |
536 | fp->f_data = (caddr_t)pnode; | |
537 | *fdflags(p, indx) &= ~UF_RESERVED; | |
91447636 A |
538 | fp_drop(p, indx, fp, 1); |
539 | proc_fdunlock(p); | |
540 | ||
541 | *retval = CAST_USER_ADDR_T(indx); | |
55e303ae | 542 | FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI); |
1c79356b A |
543 | return (0); |
544 | ||
545 | bad3: | |
546 | switch (kret) { | |
547 | case KERN_RESOURCE_SHORTAGE: | |
548 | error = ENOMEM; | |
549 | case KERN_PROTECTION_FAILURE: | |
550 | error = EACCES; | |
551 | default: | |
552 | error = EINVAL; | |
553 | } | |
554 | goto bad1; | |
555 | bad2: | |
91447636 | 556 | FREE(pnode, M_SHM); |
1c79356b | 557 | bad1: |
91447636 A |
558 | if (pinfo_alloc) |
559 | FREE(pinfo, M_SHM); | |
560 | fp_free(p, indx, nfp); | |
1c79356b | 561 | bad: |
55e303ae | 562 | FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI); |
1c79356b A |
563 | return (error); |
564 | } | |
565 | ||
91447636 A |
566 | /* |
567 | * XXX This code is repeated in several places | |
568 | */ | |
569 | static int | |
570 | psem_access(struct pseminfo *pinfo, int mode, kauth_cred_t cred) | |
1c79356b A |
571 | { |
572 | mode_t mask; | |
91447636 | 573 | int is_member; |
1c79356b A |
574 | |
575 | /* Otherwise, user id 0 always gets access. */ | |
91447636 | 576 | if (!suser(cred, NULL)) |
1c79356b A |
577 | return (0); |
578 | ||
579 | mask = 0; | |
580 | ||
581 | /* Otherwise, check the owner. */ | |
91447636 | 582 | if (kauth_cred_getuid(cred) == pinfo->psem_uid) { |
1c79356b A |
583 | if (mode & FREAD) |
584 | mask |= S_IRUSR; | |
585 | if (mode & FWRITE) | |
586 | mask |= S_IWUSR; | |
587 | return ((pinfo->psem_mode & mask) == mask ? 0 : EACCES); | |
588 | } | |
589 | ||
590 | /* Otherwise, check the groups. */ | |
91447636 A |
591 | if (kauth_cred_ismember_gid(cred, pinfo->psem_gid, &is_member) == 0 && is_member) { |
592 | if (mode & FREAD) | |
593 | mask |= S_IRGRP; | |
594 | if (mode & FWRITE) | |
595 | mask |= S_IWGRP; | |
596 | return ((pinfo->psem_mode & mask) == mask ? 0 : EACCES); | |
597 | } | |
1c79356b A |
598 | |
599 | /* Otherwise, check everyone else. */ | |
600 | if (mode & FREAD) | |
601 | mask |= S_IROTH; | |
602 | if (mode & FWRITE) | |
603 | mask |= S_IWOTH; | |
604 | return ((pinfo->psem_mode & mask) == mask ? 0 : EACCES); | |
605 | } | |
606 | ||
1c79356b | 607 | int |
91447636 | 608 | sem_unlink(__unused struct proc *p, struct sem_unlink_args *uap, __unused register_t *retval) |
1c79356b | 609 | { |
91447636 | 610 | size_t i; |
1c79356b A |
611 | int error=0; |
612 | struct psemname nd; | |
613 | struct pseminfo *pinfo; | |
1c79356b A |
614 | char * pnbuf; |
615 | char * nameptr; | |
616 | char * cp; | |
617 | size_t pathlen, plen; | |
1c79356b | 618 | int incache = 0; |
1c79356b | 619 | struct psemcache *pcache = PSEMCACHE_NULL; |
1c79356b A |
620 | |
621 | pinfo = PSEMINFO_NULL; | |
622 | ||
91447636 A |
623 | MALLOC_ZONE(pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); |
624 | if (pnbuf == NULL) { | |
625 | return(ENOSPC); /* XXX non-standard */ | |
626 | } | |
1c79356b | 627 | pathlen = MAXPATHLEN; |
91447636 | 628 | error = copyinstr(uap->name, pnbuf, MAXPATHLEN, &pathlen); |
1c79356b A |
629 | if (error) { |
630 | goto bad; | |
631 | } | |
e5568f75 | 632 | AUDIT_ARG(text, pnbuf); |
1c79356b A |
633 | if (pathlen > PSEMNAMLEN) { |
634 | error = ENAMETOOLONG; | |
635 | goto bad; | |
636 | } | |
637 | ||
638 | ||
639 | #ifdef PSXSEM_NAME_RESTRICT | |
640 | nameptr = pnbuf; | |
641 | if (*nameptr == '/') { | |
642 | while (*(nameptr++) == '/') { | |
643 | plen--; | |
644 | error = EINVAL; | |
645 | goto bad; | |
646 | } | |
647 | } else { | |
648 | error = EINVAL; | |
649 | goto bad; | |
650 | } | |
651 | #endif /* PSXSEM_NAME_RESTRICT */ | |
652 | ||
653 | plen = pathlen; | |
654 | nameptr = pnbuf; | |
655 | nd.psem_nameptr = nameptr; | |
656 | nd.psem_namelen = plen; | |
657 | nd. psem_hash =0; | |
658 | ||
659 | for (cp = nameptr, i=1; *cp != 0 && i <= plen; i++, cp++) { | |
660 | nd.psem_hash += (unsigned char)*cp * i; | |
661 | } | |
662 | ||
91447636 | 663 | PSEM_SUBSYS_LOCK(); |
1c79356b A |
664 | error = psem_cache_search(&pinfo, &nd, &pcache); |
665 | ||
666 | if (error == ENOENT) { | |
91447636 | 667 | PSEM_SUBSYS_UNLOCK(); |
1c79356b A |
668 | error = EINVAL; |
669 | goto bad; | |
670 | ||
671 | } | |
672 | if (!error) { | |
91447636 | 673 | PSEM_SUBSYS_UNLOCK(); |
1c79356b A |
674 | error = EINVAL; |
675 | goto bad; | |
676 | } else | |
677 | incache = 1; | |
91447636 A |
678 | if ( (error = psem_access(pinfo, pinfo->psem_mode, kauth_cred_get())) ) { |
679 | PSEM_SUBSYS_UNLOCK(); | |
1c79356b | 680 | goto bad; |
91447636 | 681 | } |
1c79356b A |
682 | |
683 | if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED))==0) { | |
91447636 | 684 | PSEM_SUBSYS_UNLOCK(); |
1c79356b A |
685 | return (EINVAL); |
686 | } | |
687 | ||
91447636 A |
688 | if ( (pinfo->psem_flags & PSEM_INDELETE) ) { |
689 | PSEM_SUBSYS_UNLOCK(); | |
1c79356b A |
690 | error = 0; |
691 | goto bad; | |
692 | } | |
91447636 | 693 | |
e5568f75 A |
694 | AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid, pinfo->psem_gid, |
695 | pinfo->psem_mode); | |
696 | ||
1c79356b A |
697 | pinfo->psem_flags |= PSEM_INDELETE; |
698 | pinfo->psem_usecount--; | |
699 | ||
700 | if (!pinfo->psem_usecount) { | |
701 | psem_delete(pinfo); | |
91447636 | 702 | FREE(pinfo,M_SHM); |
1c79356b A |
703 | } else |
704 | pinfo->psem_flags |= PSEM_REMOVED; | |
705 | ||
706 | psem_cache_delete(pcache); | |
91447636 A |
707 | PSEM_SUBSYS_UNLOCK(); |
708 | FREE(pcache, M_SHM); | |
1c79356b A |
709 | error = 0; |
710 | bad: | |
55e303ae | 711 | FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI); |
1c79356b A |
712 | return (error); |
713 | } | |
714 | ||
1c79356b | 715 | int |
91447636 | 716 | sem_close(struct proc *p, struct sem_close_args *uap, __unused register_t *retval) |
1c79356b | 717 | { |
91447636 A |
718 | int fd = CAST_DOWN(int,uap->sem); |
719 | struct fileproc *fp; | |
1c79356b A |
720 | int error = 0; |
721 | ||
e5568f75 | 722 | AUDIT_ARG(fd, fd); /* XXX This seems wrong; uap->sem is a pointer */ |
91447636 A |
723 | |
724 | proc_fdlock(p); | |
725 | error = fp_lookup(p,fd, &fp, 1); | |
726 | if (error) { | |
727 | proc_fdunlock(p); | |
1c79356b | 728 | return(error); |
91447636 A |
729 | } |
730 | fdrelse(p, fd); | |
731 | error = closef_locked(fp, fp->f_fglob, p); | |
732 | FREE_ZONE(fp, sizeof *fp, M_FILEPROC); | |
733 | proc_fdunlock(p); | |
734 | return(error); | |
1c79356b A |
735 | } |
736 | ||
1c79356b | 737 | int |
91447636 | 738 | sem_wait(struct proc *p, struct sem_wait_args *uap, __unused register_t *retval) |
1c79356b | 739 | { |
91447636 A |
740 | int fd = CAST_DOWN(int,uap->sem); |
741 | struct fileproc *fp; | |
1c79356b A |
742 | struct pseminfo * pinfo; |
743 | struct psemnode * pnode ; | |
744 | kern_return_t kret; | |
745 | int error; | |
746 | ||
91447636 A |
747 | error = fp_getfpsem(p, fd, &fp, &pnode); |
748 | if (error) | |
1c79356b | 749 | return (error); |
91447636 A |
750 | if (((pnode = (struct psemnode *)fp->f_data)) == PSEMNODE_NULL ) { |
751 | error = EINVAL; | |
752 | goto out; | |
753 | } | |
754 | PSEM_SUBSYS_LOCK(); | |
755 | if ((pinfo = pnode->pinfo) == PSEMINFO_NULL) { | |
756 | PSEM_SUBSYS_UNLOCK(); | |
757 | error = EINVAL; | |
758 | goto out; | |
759 | } | |
1c79356b A |
760 | if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED)) |
761 | != PSEM_ALLOCATED) { | |
91447636 A |
762 | PSEM_SUBSYS_UNLOCK(); |
763 | error = EINVAL; | |
764 | goto out; | |
1c79356b A |
765 | } |
766 | ||
91447636 | 767 | PSEM_SUBSYS_UNLOCK(); |
1c79356b A |
768 | kret = semaphore_wait(pinfo->psem_semobject); |
769 | switch (kret) { | |
770 | case KERN_INVALID_ADDRESS: | |
771 | case KERN_PROTECTION_FAILURE: | |
91447636 A |
772 | error = EACCES; |
773 | break; | |
1c79356b A |
774 | case KERN_ABORTED: |
775 | case KERN_OPERATION_TIMED_OUT: | |
91447636 A |
776 | error = EINTR; |
777 | break; | |
1c79356b | 778 | case KERN_SUCCESS: |
91447636 A |
779 | error = 0; |
780 | break; | |
1c79356b | 781 | default: |
91447636 A |
782 | error = EINVAL; |
783 | break; | |
1c79356b | 784 | } |
91447636 A |
785 | out: |
786 | fp_drop(p, fd, fp, 0); | |
787 | return(error); | |
1c79356b | 788 | |
91447636 | 789 | } |
1c79356b A |
790 | |
791 | int | |
91447636 | 792 | sem_trywait(struct proc *p, struct sem_trywait_args *uap, __unused register_t *retval) |
1c79356b | 793 | { |
91447636 A |
794 | int fd = CAST_DOWN(int,uap->sem); |
795 | struct fileproc *fp; | |
1c79356b A |
796 | struct pseminfo * pinfo; |
797 | struct psemnode * pnode ; | |
798 | kern_return_t kret; | |
799 | mach_timespec_t wait_time; | |
800 | int error; | |
801 | ||
91447636 A |
802 | error = fp_getfpsem(p, fd, &fp, &pnode); |
803 | if (error) | |
1c79356b | 804 | return (error); |
91447636 A |
805 | if (((pnode = (struct psemnode *)fp->f_data)) == PSEMNODE_NULL ) { |
806 | error = EINVAL; | |
807 | goto out; | |
808 | } | |
809 | PSEM_SUBSYS_LOCK(); | |
810 | if ((pinfo = pnode->pinfo) == PSEMINFO_NULL) { | |
811 | PSEM_SUBSYS_UNLOCK(); | |
812 | error = EINVAL; | |
813 | goto out; | |
814 | } | |
1c79356b A |
815 | if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED)) |
816 | != PSEM_ALLOCATED) { | |
91447636 A |
817 | PSEM_SUBSYS_UNLOCK(); |
818 | error = EINVAL; | |
819 | goto out; | |
1c79356b A |
820 | } |
821 | ||
91447636 | 822 | PSEM_SUBSYS_UNLOCK(); |
1c79356b A |
823 | wait_time.tv_sec = 0; |
824 | wait_time.tv_nsec = 0; | |
825 | ||
826 | kret = semaphore_timedwait(pinfo->psem_semobject, MACH_TIMESPEC_ZERO); | |
827 | switch (kret) { | |
828 | case KERN_INVALID_ADDRESS: | |
829 | case KERN_PROTECTION_FAILURE: | |
91447636 A |
830 | error = EINVAL; |
831 | break; | |
1c79356b | 832 | case KERN_ABORTED: |
91447636 A |
833 | error = EINTR; |
834 | break; | |
1c79356b | 835 | case KERN_OPERATION_TIMED_OUT: |
91447636 A |
836 | error = EAGAIN; |
837 | break; | |
1c79356b | 838 | case KERN_SUCCESS: |
91447636 A |
839 | error = 0; |
840 | break; | |
1c79356b | 841 | default: |
91447636 A |
842 | error = EINVAL; |
843 | break; | |
1c79356b | 844 | } |
91447636 A |
845 | out: |
846 | fp_drop(p, fd, fp, 0); | |
847 | return(error); | |
1c79356b A |
848 | } |
849 | ||
1c79356b | 850 | int |
91447636 | 851 | sem_post(struct proc *p, struct sem_post_args *uap, __unused register_t *retval) |
1c79356b | 852 | { |
91447636 A |
853 | int fd = CAST_DOWN(int,uap->sem); |
854 | struct fileproc *fp; | |
1c79356b A |
855 | struct pseminfo * pinfo; |
856 | struct psemnode * pnode ; | |
857 | kern_return_t kret; | |
858 | int error; | |
859 | ||
91447636 A |
860 | error = fp_getfpsem(p, fd, &fp, &pnode); |
861 | if (error) | |
1c79356b | 862 | return (error); |
91447636 A |
863 | if (((pnode = (struct psemnode *)fp->f_data)) == PSEMNODE_NULL ) { |
864 | error = EINVAL; | |
865 | goto out; | |
866 | } | |
867 | PSEM_SUBSYS_LOCK(); | |
868 | if ((pinfo = pnode->pinfo) == PSEMINFO_NULL) { | |
869 | PSEM_SUBSYS_UNLOCK(); | |
870 | error = EINVAL; | |
871 | goto out; | |
872 | } | |
1c79356b A |
873 | if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED)) |
874 | != PSEM_ALLOCATED) { | |
91447636 A |
875 | PSEM_SUBSYS_UNLOCK(); |
876 | error = EINVAL; | |
877 | goto out; | |
1c79356b A |
878 | } |
879 | ||
91447636 | 880 | PSEM_SUBSYS_UNLOCK(); |
1c79356b A |
881 | kret = semaphore_signal(pinfo->psem_semobject); |
882 | switch (kret) { | |
883 | case KERN_INVALID_ADDRESS: | |
884 | case KERN_PROTECTION_FAILURE: | |
91447636 A |
885 | error = EINVAL; |
886 | break; | |
1c79356b A |
887 | case KERN_ABORTED: |
888 | case KERN_OPERATION_TIMED_OUT: | |
91447636 A |
889 | error = EINTR; |
890 | break; | |
1c79356b | 891 | case KERN_SUCCESS: |
91447636 A |
892 | error = 0; |
893 | break; | |
1c79356b | 894 | default: |
91447636 A |
895 | error = EINVAL; |
896 | break; | |
1c79356b | 897 | } |
91447636 A |
898 | out: |
899 | fp_drop(p, fd, fp, 0); | |
900 | return(error); | |
1c79356b A |
901 | } |
902 | ||
1c79356b | 903 | int |
91447636 | 904 | sem_init(__unused struct proc *p, __unused struct sem_init_args *uap, __unused register_t *retval) |
1c79356b A |
905 | { |
906 | return(ENOSYS); | |
907 | } | |
908 | ||
1c79356b | 909 | int |
91447636 | 910 | sem_destroy(__unused struct proc *p, __unused struct sem_destroy_args *uap, __unused register_t *retval) |
1c79356b A |
911 | { |
912 | return(ENOSYS); | |
913 | } | |
914 | ||
1c79356b | 915 | int |
91447636 | 916 | sem_getvalue(__unused struct proc *p, __unused struct sem_getvalue_args *uap, __unused register_t *retval) |
1c79356b A |
917 | { |
918 | return(ENOSYS); | |
919 | } | |
920 | ||
9bccf70c | 921 | static int |
91447636 A |
922 | psem_close(struct psemnode *pnode, __unused int flags, |
923 | __unused kauth_cred_t cred, __unused struct proc *p) | |
1c79356b A |
924 | { |
925 | int error=0; | |
1c79356b A |
926 | register struct pseminfo *pinfo; |
927 | ||
91447636 A |
928 | PSEM_SUBSYS_LOCK(); |
929 | if ((pinfo = pnode->pinfo) == PSEMINFO_NULL){ | |
930 | PSEM_SUBSYS_UNLOCK(); | |
1c79356b | 931 | return(EINVAL); |
91447636 | 932 | } |
1c79356b A |
933 | |
934 | if ((pinfo->psem_flags & PSEM_ALLOCATED) != PSEM_ALLOCATED) { | |
91447636 | 935 | PSEM_SUBSYS_UNLOCK(); |
1c79356b A |
936 | return(EINVAL); |
937 | } | |
938 | #if DIAGNOSTIC | |
939 | if(!pinfo->psem_usecount) { | |
940 | kprintf("negative usecount in psem_close\n"); | |
941 | } | |
942 | #endif /* DIAGNOSTIC */ | |
943 | pinfo->psem_usecount--; | |
944 | ||
945 | if ((pinfo->psem_flags & PSEM_REMOVED) && !pinfo->psem_usecount) { | |
91447636 A |
946 | PSEM_SUBSYS_UNLOCK(); |
947 | /* lock dropped as only semaphore is destroyed here */ | |
1c79356b | 948 | error = psem_delete(pinfo); |
91447636 A |
949 | FREE(pinfo,M_SHM); |
950 | } else { | |
951 | PSEM_SUBSYS_UNLOCK(); | |
1c79356b | 952 | } |
91447636 A |
953 | /* subsystem lock is dropped when we get here */ |
954 | FREE(pnode, M_SHM); | |
1c79356b A |
955 | return (error); |
956 | } | |
957 | ||
9bccf70c | 958 | static int |
91447636 A |
959 | psem_closefile(fg, p) |
960 | struct fileglob *fg; | |
9bccf70c A |
961 | struct proc *p; |
962 | { | |
91447636 | 963 | int error; |
9bccf70c | 964 | |
91447636 A |
965 | /* Not locked as psem_close is called only from here and is locked properly */ |
966 | error = psem_close(((struct psemnode *)fg->fg_data), fg->fg_flag, | |
967 | fg->fg_cred, p); | |
968 | ||
969 | return(error); | |
9bccf70c A |
970 | } |
971 | ||
91447636 | 972 | static int |
1c79356b A |
973 | psem_delete(struct pseminfo * pinfo) |
974 | { | |
975 | kern_return_t kret; | |
976 | ||
977 | kret = semaphore_destroy(kernel_task, pinfo->psem_semobject); | |
978 | ||
979 | switch (kret) { | |
980 | case KERN_INVALID_ADDRESS: | |
981 | case KERN_PROTECTION_FAILURE: | |
982 | return (EINVAL); | |
983 | case KERN_ABORTED: | |
984 | case KERN_OPERATION_TIMED_OUT: | |
985 | return (EINTR); | |
986 | case KERN_SUCCESS: | |
987 | return(0); | |
988 | default: | |
989 | return (EINVAL); | |
990 | } | |
1c79356b A |
991 | } |
992 | ||
9bccf70c | 993 | static int |
91447636 A |
994 | psem_read(__unused struct fileproc *fp, __unused struct uio *uio, |
995 | __unused kauth_cred_t cred, __unused int flags, | |
996 | __unused struct proc *p) | |
1c79356b | 997 | { |
91447636 | 998 | return(ENOTSUP); |
1c79356b | 999 | } |
9bccf70c A |
1000 | |
1001 | static int | |
91447636 A |
1002 | psem_write(__unused struct fileproc *fp, __unused struct uio *uio, |
1003 | __unused kauth_cred_t cred, __unused int flags, | |
1004 | __unused struct proc *p) | |
1c79356b | 1005 | { |
91447636 | 1006 | return(ENOTSUP); |
1c79356b | 1007 | } |
9bccf70c A |
1008 | |
1009 | static int | |
91447636 A |
1010 | psem_ioctl(__unused struct fileproc *fp, __unused u_long com, |
1011 | __unused caddr_t data, __unused struct proc *p) | |
1c79356b | 1012 | { |
91447636 | 1013 | return(ENOTSUP); |
1c79356b | 1014 | } |
9bccf70c A |
1015 | |
1016 | static int | |
91447636 A |
1017 | psem_select(__unused struct fileproc *fp, __unused int which, |
1018 | __unused void *wql, __unused struct proc *p) | |
1c79356b | 1019 | { |
91447636 | 1020 | return(ENOTSUP); |
1c79356b | 1021 | } |
55e303ae A |
1022 | |
1023 | static int | |
91447636 A |
1024 | psem_kqfilter(__unused struct fileproc *fp, __unused struct knote *kn, |
1025 | __unused struct proc *p) | |
55e303ae | 1026 | { |
91447636 | 1027 | return (ENOTSUP); |
55e303ae A |
1028 | } |
1029 |