]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
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. | |
11 | * | |
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 | |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
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. | |
19 | * | |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* | |
23 | * Copyright (c) 1990, 1996-1998 Apple Computer, Inc. | |
24 | * All Rights Reserved. | |
25 | */ | |
26 | /* | |
27 | * posix_shm.c : Support for POSIX shared memory apis | |
28 | * | |
29 | * File: posix_shm.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> | |
42 | #include <sys/file.h> | |
43 | #include <sys/filedesc.h> | |
44 | #include <sys/stat.h> | |
45 | #include <sys/buf.h> | |
46 | #include <sys/proc.h> | |
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/mman.h> | |
54 | #include <sys/stat.h> | |
55 | #include <mach/mach_types.h> | |
56 | #include <mach/vm_prot.h> | |
57 | #include <mach/vm_inherit.h> | |
58 | #include <mach/kern_return.h> | |
59 | #include <mach/memory_object_control.h> | |
60 | ||
61 | ||
62 | #define PSHMNAMLEN 31 /* maximum name segment length we bother with */ | |
63 | ||
64 | struct pshminfo { | |
65 | unsigned int pshm_flags; | |
66 | unsigned int pshm_usecount; | |
67 | off_t pshm_length; | |
68 | mode_t pshm_mode; | |
69 | uid_t pshm_uid; | |
70 | gid_t pshm_gid; | |
71 | char pshm_name[PSHMNAMLEN + 1]; /* segment name */ | |
72 | void * pshm_memobject; | |
73 | #if DIAGNOSTIC | |
74 | unsigned int pshm_readcount; | |
75 | unsigned int pshm_writecount; | |
76 | struct proc * pshm_proc; | |
77 | #endif /* DIAGNOSTIC */ | |
78 | }; | |
79 | #define PSHMINFO_NULL (struct pshminfo *)0 | |
80 | ||
81 | #define PSHM_NONE 1 | |
82 | #define PSHM_DEFINED 2 | |
83 | #define PSHM_ALLOCATED 4 | |
84 | #define PSHM_MAPPED 8 | |
85 | #define PSHM_INUSE 0x10 | |
86 | #define PSHM_REMOVED 0x20 | |
87 | #define PSHM_INCREATE 0x40 | |
88 | #define PSHM_INDELETE 0x80 | |
89 | ||
90 | struct pshmcache { | |
91 | LIST_ENTRY(pshmcache) pshm_hash; /* hash chain */ | |
92 | struct pshminfo *pshminfo; /* vnode the name refers to */ | |
93 | int pshm_nlen; /* length of name */ | |
94 | char pshm_name[PSHMNAMLEN + 1]; /* segment name */ | |
95 | }; | |
96 | #define PSHMCACHE_NULL (struct pshmcache *)0 | |
97 | ||
98 | struct pshmstats { | |
99 | long goodhits; /* hits that we can really use */ | |
100 | long neghits; /* negative hits that we can use */ | |
101 | long badhits; /* hits we must drop */ | |
102 | long falsehits; /* hits with id mismatch */ | |
103 | long miss; /* misses */ | |
104 | long longnames; /* long names that ignore cache */ | |
105 | }; | |
106 | ||
107 | struct pshmname { | |
108 | char *pshm_nameptr; /* pointer to looked up name */ | |
109 | long pshm_namelen; /* length of looked up component */ | |
110 | u_long pshm_hash; /* hash value of looked up name */ | |
111 | }; | |
112 | ||
113 | struct pshmnode { | |
114 | off_t mapp_addr; | |
115 | size_t map_size; | |
116 | struct pshminfo *pinfo; | |
117 | unsigned int pshm_usecount; | |
118 | #if DIAGNOSTIC | |
119 | unsigned int readcnt; | |
120 | unsigned int writecnt; | |
121 | #endif | |
122 | }; | |
123 | #define PSHMNODE_NULL (struct pshmnode *)0 | |
124 | ||
125 | ||
126 | #define PSHMHASH(pnp) \ | |
127 | (&pshmhashtbl[(pnp)->pshm_hash & pshmhash]) | |
128 | LIST_HEAD(pshmhashhead, pshmcache) *pshmhashtbl; /* Hash Table */ | |
129 | u_long pshmhash; /* size of hash table - 1 */ | |
130 | long pshmnument; /* number of cache entries allocated */ | |
131 | struct pshmstats pshmstats; /* cache effectiveness statistics */ | |
132 | ||
133 | int pshm_read __P((struct file *fp, struct uio *uio, | |
134 | struct ucred *cred)); | |
135 | int pshm_write __P((struct file *fp, struct uio *uio, | |
136 | struct ucred *cred)); | |
137 | int pshm_ioctl __P((struct file *fp, u_long com, | |
138 | caddr_t data, struct proc *p)); | |
139 | int pshm_select __P((struct file *fp, int which, | |
140 | struct proc *p)); | |
141 | int pshm_closefile __P((struct file *fp, struct proc *p)); | |
142 | ||
143 | struct fileops pshmops = | |
144 | { pshm_read, pshm_write, pshm_ioctl, pshm_select, pshm_closefile }; | |
145 | ||
146 | ||
147 | ||
148 | /* | |
149 | * Lookup an entry in the cache | |
150 | * | |
151 | * | |
152 | * status of -1 is returned if matches | |
153 | * If the lookup determines that the name does not exist | |
154 | * (negative cacheing), a status of ENOENT is returned. If the lookup | |
155 | * fails, a status of zero is returned. | |
156 | */ | |
157 | ||
158 | int | |
159 | pshm_cache_search(pshmp, pnp, pcache) | |
160 | struct pshminfo **pshmp; | |
161 | struct pshmname *pnp; | |
162 | struct pshmcache **pcache; | |
163 | { | |
164 | register struct pshmcache *pcp, *nnp; | |
165 | register struct pshmhashhead *pcpp; | |
166 | ||
167 | if (pnp->pshm_namelen > PSHMNAMLEN) { | |
168 | pshmstats.longnames++; | |
169 | return (0); | |
170 | } | |
171 | ||
172 | pcpp = PSHMHASH(pnp); | |
173 | for (pcp = pcpp->lh_first; pcp != 0; pcp = nnp) { | |
174 | nnp = pcp->pshm_hash.le_next; | |
175 | if (pcp->pshm_nlen == pnp->pshm_namelen && | |
176 | !bcmp(pcp->pshm_name, pnp->pshm_nameptr, (u_int)pcp-> pshm_nlen)) | |
177 | break; | |
178 | } | |
179 | ||
180 | if (pcp == 0) { | |
181 | pshmstats.miss++; | |
182 | return (0); | |
183 | } | |
184 | ||
185 | /* We found a "positive" match, return the vnode */ | |
186 | if (pcp->pshminfo) { | |
187 | pshmstats.goodhits++; | |
188 | /* TOUCH(ncp); */ | |
189 | *pshmp = pcp->pshminfo; | |
190 | *pcache = pcp; | |
191 | return (-1); | |
192 | } | |
193 | ||
194 | /* | |
195 | * We found a "negative" match, ENOENT notifies client of this match. | |
196 | * The nc_vpid field records whether this is a whiteout. | |
197 | */ | |
198 | pshmstats.neghits++; | |
199 | return (ENOENT); | |
200 | } | |
201 | ||
202 | /* | |
203 | * Add an entry to the cache. | |
204 | */ | |
205 | int | |
206 | pshm_cache_add(pshmp, pnp) | |
207 | struct pshminfo *pshmp; | |
208 | struct pshmname *pnp; | |
209 | { | |
210 | register struct pshmcache *pcp; | |
211 | register struct pshmhashhead *pcpp; | |
212 | register struct pshminfo *dpinfo; | |
213 | register struct pshmcache *dpcp; | |
214 | ||
215 | #if DIAGNOSTIC | |
216 | if (pnp->pshm_namelen > NCHNAMLEN) | |
217 | panic("cache_enter: name too long"); | |
218 | #endif | |
219 | ||
220 | /* | |
221 | * We allocate a new entry if we are less than the maximum | |
222 | * allowed and the one at the front of the LRU list is in use. | |
223 | * Otherwise we use the one at the front of the LRU list. | |
224 | */ | |
225 | pcp = (struct pshmcache *)_MALLOC(sizeof(struct pshmcache), M_SHM, M_WAITOK); | |
226 | /* if the entry has already been added by some one else return */ | |
227 | if (pshm_cache_search(&dpinfo, pnp, &dpcp) == -1) { | |
228 | _FREE(pcp, M_SHM); | |
229 | return(EEXIST); | |
230 | } | |
231 | pshmnument++; | |
232 | ||
233 | bzero(pcp, sizeof(struct pshmcache)); | |
234 | /* | |
235 | * Fill in cache info, if vp is NULL this is a "negative" cache entry. | |
236 | * For negative entries, we have to record whether it is a whiteout. | |
237 | * the whiteout flag is stored in the nc_vpid field which is | |
238 | * otherwise unused. | |
239 | */ | |
240 | pcp->pshminfo = pshmp; | |
241 | pcp->pshm_nlen = pnp->pshm_namelen; | |
242 | bcopy(pnp->pshm_nameptr, pcp->pshm_name, (unsigned)pcp->pshm_nlen); | |
243 | pcpp = PSHMHASH(pnp); | |
244 | #if DIAGNOSTIC | |
245 | { | |
246 | register struct pshmcache *p; | |
247 | ||
248 | for (p = pcpp->lh_first; p != 0; p = p->pshm_hash.le_next) | |
249 | if (p == pcp) | |
250 | panic("cache_enter: duplicate"); | |
251 | } | |
252 | #endif | |
253 | LIST_INSERT_HEAD(pcpp, pcp, pshm_hash); | |
254 | return(0); | |
255 | } | |
256 | ||
257 | /* | |
258 | * Name cache initialization, from vfs_init() when we are booting | |
259 | */ | |
260 | void | |
261 | pshm_cache_init() | |
262 | { | |
263 | pshmhashtbl = hashinit(desiredvnodes, M_SHM, &pshmhash); | |
264 | } | |
265 | ||
266 | /* | |
267 | * Invalidate a all entries to particular vnode. | |
268 | * | |
269 | * We actually just increment the v_id, that will do it. The entries will | |
270 | * be purged by lookup as they get found. If the v_id wraps around, we | |
271 | * need to ditch the entire cache, to avoid confusion. No valid vnode will | |
272 | * ever have (v_id == 0). | |
273 | */ | |
274 | void | |
275 | pshm_cache_purge(void) | |
276 | { | |
277 | struct pshmcache *pcp; | |
278 | struct pshmhashhead *pcpp; | |
279 | ||
280 | for (pcpp = &pshmhashtbl[pshmhash]; pcpp >= pshmhashtbl; pcpp--) { | |
281 | while (pcp = pcpp->lh_first) | |
282 | pshm_cache_delete(pcp); | |
283 | } | |
284 | } | |
285 | ||
286 | pshm_cache_delete(pcp) | |
287 | struct pshmcache *pcp; | |
288 | { | |
289 | #if DIAGNOSTIC | |
290 | if (pcp->pshm_hash.le_prev == 0) | |
291 | panic("namecache purge le_prev"); | |
292 | if (pcp->pshm_hash.le_next == pcp) | |
293 | panic("namecache purge le_next"); | |
294 | #endif /* DIAGNOSTIC */ | |
295 | LIST_REMOVE(pcp, pshm_hash); | |
296 | pcp->pshm_hash.le_prev = 0; | |
297 | pshmnument--; | |
298 | } | |
299 | ||
300 | ||
301 | struct shm_open_args { | |
302 | const char *name; | |
303 | int oflag; | |
304 | int mode; | |
305 | }; | |
306 | ||
307 | int | |
308 | shm_open(p, uap, retval) | |
309 | struct proc *p; | |
310 | register struct shm_open_args *uap; | |
311 | register_t *retval; | |
312 | { | |
313 | register struct filedesc *fdp = p->p_fd; | |
314 | register struct file *fp; | |
315 | register struct vnode *vp; | |
316 | int i; | |
317 | struct file *nfp; | |
318 | int type, indx, error; | |
319 | struct pshmname nd; | |
320 | struct pshminfo *pinfo; | |
321 | extern struct fileops pshmops; | |
322 | char * pnbuf; | |
323 | char * nameptr; | |
324 | char * cp; | |
325 | size_t pathlen, plen; | |
326 | int fmode ; | |
327 | int cmode = uap->mode; | |
328 | int incache = 0; | |
329 | struct pshmnode * pnode = PSHMNODE_NULL; | |
330 | struct pshmcache * pcache = PSHMCACHE_NULL; | |
331 | ||
332 | ||
333 | pinfo = PSHMINFO_NULL; | |
334 | ||
335 | MALLOC_ZONE(pnbuf, caddr_t, | |
336 | MAXPATHLEN, M_NAMEI, M_WAITOK); | |
337 | pathlen = MAXPATHLEN; | |
338 | error = copyinstr(uap->name, pnbuf, | |
339 | MAXPATHLEN, &pathlen); | |
340 | if (error) { | |
341 | goto bad; | |
342 | } | |
343 | if (pathlen > PSHMNAMLEN) { | |
344 | error = ENAMETOOLONG; | |
345 | goto bad; | |
346 | } | |
347 | ||
348 | ||
349 | #ifdef PSXSHM_NAME_RESTRICT | |
350 | nameptr = pnbuf; | |
351 | if (*nameptr == '/') { | |
352 | while (*(nameptr++) == '/') { | |
353 | plen--; | |
354 | error = EINVAL; | |
355 | goto bad; | |
356 | } | |
357 | } else { | |
358 | error = EINVAL; | |
359 | goto bad; | |
360 | } | |
361 | #endif /* PSXSHM_NAME_RESTRICT */ | |
362 | ||
363 | plen = pathlen; | |
364 | nameptr = pnbuf; | |
365 | nd.pshm_nameptr = nameptr; | |
366 | nd.pshm_namelen = plen; | |
367 | nd. pshm_hash =0; | |
368 | ||
369 | for (cp = nameptr, i=1; *cp != 0 && i <= plen; i++, cp++) { | |
370 | nd.pshm_hash += (unsigned char)*cp * i; | |
371 | } | |
372 | ||
373 | error = pshm_cache_search(&pinfo, &nd, &pcache); | |
374 | ||
375 | if (error == ENOENT) { | |
376 | error = EINVAL; | |
377 | goto bad; | |
378 | ||
379 | } | |
380 | if (!error) { | |
381 | incache = 0; | |
382 | } else | |
383 | incache = 1; | |
384 | fmode = FFLAGS(uap->oflag); | |
385 | if ((fmode & (FREAD | FWRITE))==0) | |
386 | return(EINVAL); | |
387 | ||
388 | if (error = falloc(p, &nfp, &indx)) | |
389 | return (error); | |
390 | fp = nfp; | |
391 | ||
392 | cmode &= ALLPERMS; | |
393 | ||
394 | if (fmode & O_CREAT) { | |
395 | if ((fmode & O_EXCL) && incache) { | |
396 | /* shm obj exists and opened O_EXCL */ | |
397 | #if notyet | |
398 | if (pinfo->pshm_flags & PSHM_INDELETE) { | |
399 | } | |
400 | #endif | |
401 | error = EEXIST; | |
402 | goto bad; | |
403 | } | |
404 | if (!incache) { | |
405 | /* create a new one */ | |
406 | pinfo = (struct pshminfo *)_MALLOC(sizeof(struct pshminfo), M_SHM, M_WAITOK); | |
407 | bzero(pinfo, sizeof(struct pshminfo)); | |
408 | pinfo->pshm_flags = PSHM_DEFINED | PSHM_INCREATE; | |
409 | pinfo->pshm_usecount = 1; | |
410 | pinfo->pshm_mode = cmode; | |
411 | pinfo->pshm_uid = p->p_ucred->cr_uid; | |
412 | pinfo->pshm_gid = p->p_ucred->cr_gid; | |
413 | } else { | |
414 | /* already exists */ | |
415 | if( pinfo->pshm_flags & PSHM_INDELETE) { | |
416 | error = ENOENT; | |
417 | goto bad; | |
418 | } | |
419 | if (error = pshm_access(pinfo, fmode, p->p_ucred, p)) | |
420 | goto bad; | |
421 | } | |
422 | } else { | |
423 | if (!incache) { | |
424 | /* O_CREAT is not set and the shm obecj does not exist */ | |
425 | error = ENOENT; | |
426 | goto bad; | |
427 | } | |
428 | if( pinfo->pshm_flags & PSHM_INDELETE) { | |
429 | error = ENOENT; | |
430 | goto bad; | |
431 | } | |
432 | if (error = pshm_access(pinfo, fmode, p->p_ucred, p)) | |
433 | goto bad; | |
434 | } | |
435 | if (fmode & O_TRUNC) { | |
436 | error = EINVAL; | |
437 | goto bad1; | |
438 | } | |
439 | #if DIAGNOSTIC | |
440 | if (fmode & FWRITE) | |
441 | pinfo->pshm_writecount++; | |
442 | if (fmode & FREAD) | |
443 | pinfo->pshm_readcount++; | |
444 | #endif | |
445 | pnode = (struct pshmnode *)_MALLOC(sizeof(struct pshmnode), M_SHM, M_WAITOK); | |
446 | bzero(pnode, sizeof(struct pshmnode)); | |
447 | ||
448 | if (!incache) { | |
449 | if (error = pshm_cache_add(pinfo, &nd)) { | |
450 | goto bad2; | |
451 | } | |
452 | } | |
453 | pinfo->pshm_flags &= ~PSHM_INCREATE; | |
454 | pinfo->pshm_usecount++; | |
455 | pnode->pinfo = pinfo; | |
456 | fp->f_flag = fmode & FMASK; | |
457 | fp->f_type = DTYPE_PSXSHM; | |
458 | fp->f_ops = &pshmops; | |
459 | fp->f_data = (caddr_t)pnode; | |
460 | *fdflags(p, indx) &= ~UF_RESERVED; | |
461 | *retval = indx; | |
462 | _FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI); | |
463 | return (0); | |
464 | bad2: | |
465 | _FREE(pnode, M_SHM); | |
466 | ||
467 | bad1: | |
468 | _FREE(pinfo, M_SHM); | |
469 | ||
470 | bad: | |
471 | _FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI); | |
472 | return (error); | |
473 | } | |
474 | ||
475 | ||
476 | /* ARGSUSED */ | |
477 | int | |
478 | pshm_truncate(p, fp, fd, length, retval) | |
479 | struct proc *p; | |
480 | struct file *fp; | |
481 | int fd; | |
482 | off_t length; | |
483 | register_t *retval; | |
484 | { | |
485 | struct pshminfo * pinfo; | |
486 | struct pshmnode * pnode ; | |
487 | kern_return_t kret; | |
488 | vm_offset_t user_addr; | |
489 | void * mem_object; | |
490 | vm_size_t size; | |
491 | ||
492 | if (fp->f_type != DTYPE_PSXSHM) { | |
493 | return(EINVAL); | |
494 | } | |
495 | ||
496 | ||
497 | if (((pnode = (struct pshmnode *)fp->f_data)) == PSHMNODE_NULL ) | |
498 | return(EINVAL); | |
499 | ||
500 | if ((pinfo = pnode->pinfo) == PSHMINFO_NULL) | |
501 | return(EINVAL); | |
502 | if ((pinfo->pshm_flags & (PSHM_DEFINED | PSHM_ALLOCATED)) | |
503 | != PSHM_DEFINED) { | |
504 | return(EINVAL); | |
505 | } | |
506 | ||
507 | size = round_page (length); | |
508 | kret = vm_allocate(current_map(), &user_addr, size, TRUE); | |
509 | if (kret != KERN_SUCCESS) | |
510 | goto out; | |
511 | ||
512 | kret = mach_make_memory_entry (current_map(), &size, | |
513 | user_addr, VM_PROT_DEFAULT, &mem_object, 0); | |
514 | ||
515 | if (kret != KERN_SUCCESS) | |
516 | goto out; | |
517 | ||
518 | vm_deallocate(current_map(), user_addr, size); | |
519 | ||
520 | pinfo->pshm_flags &= ~PSHM_DEFINED; | |
521 | pinfo->pshm_flags = PSHM_ALLOCATED; | |
522 | pinfo->pshm_memobject = mem_object; | |
523 | pinfo->pshm_length = size; | |
524 | return(0); | |
525 | ||
526 | out: | |
527 | switch (kret) { | |
528 | case KERN_INVALID_ADDRESS: | |
529 | case KERN_NO_SPACE: | |
530 | return (ENOMEM); | |
531 | case KERN_PROTECTION_FAILURE: | |
532 | return (EACCES); | |
533 | default: | |
534 | return (EINVAL); | |
535 | ||
536 | } | |
537 | } | |
538 | ||
539 | int | |
540 | pshm_stat(pnode, sb) | |
541 | struct pshmnode *pnode; | |
542 | struct stat *sb; | |
543 | { | |
544 | struct pshminfo *pinfo; | |
545 | ||
546 | if ((pinfo = pnode->pinfo) == PSHMINFO_NULL) | |
547 | return(EINVAL); | |
548 | ||
549 | bzero(sb, sizeof(struct stat)); | |
550 | sb->st_mode = pinfo->pshm_mode; | |
551 | sb->st_uid = pinfo->pshm_uid; | |
552 | sb->st_gid = pinfo->pshm_gid; | |
553 | sb->st_size = pinfo->pshm_length; | |
554 | ||
555 | return(0); | |
556 | } | |
557 | ||
558 | int | |
559 | pshm_access(struct pshminfo *pinfo, int mode, struct ucred *cred, struct proc *p) | |
560 | { | |
561 | mode_t mask; | |
562 | register gid_t *gp; | |
563 | int i, error; | |
564 | ||
565 | /* Otherwise, user id 0 always gets access. */ | |
566 | if (cred->cr_uid == 0) | |
567 | return (0); | |
568 | ||
569 | mask = 0; | |
570 | ||
571 | /* Otherwise, check the owner. */ | |
572 | if (cred->cr_uid == pinfo->pshm_uid) { | |
573 | if (mode & FREAD) | |
574 | mask |= S_IRUSR; | |
575 | if (mode & FWRITE) | |
576 | mask |= S_IWUSR; | |
577 | return ((pinfo->pshm_mode & mask) == mask ? 0 : EACCES); | |
578 | } | |
579 | ||
580 | /* Otherwise, check the groups. */ | |
581 | for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) | |
582 | if (pinfo->pshm_gid == *gp) { | |
583 | if (mode & FREAD) | |
584 | mask |= S_IRGRP; | |
585 | if (mode & FWRITE) | |
586 | mask |= S_IWGRP; | |
587 | return ((pinfo->pshm_mode & mask) == mask ? 0 : EACCES); | |
588 | } | |
589 | ||
590 | /* Otherwise, check everyone else. */ | |
591 | if (mode & FREAD) | |
592 | mask |= S_IROTH; | |
593 | if (mode & FWRITE) | |
594 | mask |= S_IWOTH; | |
595 | return ((pinfo->pshm_mode & mask) == mask ? 0 : EACCES); | |
596 | } | |
597 | struct mmap_args { | |
598 | caddr_t addr; | |
599 | size_t len; | |
600 | int prot; | |
601 | int flags; | |
602 | int fd; | |
603 | #ifdef DOUBLE_ALIGN_PARAMS | |
604 | long pad; | |
605 | #endif | |
606 | off_t pos; | |
607 | }; | |
608 | ||
609 | int | |
610 | pshm_mmap(struct proc *p, struct mmap_args *uap, register_t *retval, struct file *fp, vm_size_t pageoff) | |
611 | { | |
612 | vm_offset_t user_addr = uap->addr; | |
613 | vm_size_t user_size = uap->len ; | |
614 | int prot = uap->prot; | |
615 | int flags = uap->flags; | |
616 | vm_object_offset_t file_pos = (vm_object_offset_t)uap->pos; | |
617 | int fd = uap->fd; | |
618 | vm_map_t user_map; | |
619 | boolean_t find_space,docow; | |
620 | kern_return_t kret; | |
621 | struct pshminfo * pinfo; | |
622 | struct pshmnode * pnode; | |
623 | void * mem_object; | |
624 | ||
625 | if (user_size == 0) | |
626 | return(0); | |
627 | ||
628 | if ((flags & MAP_SHARED) == 0) | |
629 | return(EINVAL); | |
630 | ||
631 | ||
632 | if ((prot & PROT_WRITE) && ((fp->f_flag & FWRITE) == 0)) { | |
633 | return(EPERM); | |
634 | } | |
635 | ||
636 | if (((pnode = (struct pshmnode *)fp->f_data)) == PSHMNODE_NULL ) | |
637 | return(EINVAL); | |
638 | ||
639 | if ((pinfo = pnode->pinfo) == PSHMINFO_NULL) | |
640 | return(EINVAL); | |
641 | ||
642 | if ((pinfo->pshm_flags & PSHM_ALLOCATED) != PSHM_ALLOCATED) { | |
643 | return(EINVAL); | |
644 | } | |
645 | if (user_size > pinfo->pshm_length) { | |
646 | return(EINVAL); | |
647 | } | |
648 | if ((off_t)user_size + file_pos > pinfo->pshm_length) { | |
649 | return(EINVAL); | |
650 | } | |
651 | if ((mem_object = pinfo->pshm_memobject) == NULL) { | |
652 | return(EINVAL); | |
653 | } | |
654 | ||
655 | ||
656 | user_map = current_map(); | |
657 | ||
658 | if ((flags & MAP_FIXED) == 0) { | |
659 | find_space = TRUE; | |
660 | user_addr = round_page(user_addr); | |
661 | } else { | |
662 | if (user_addr != trunc_page(user_addr)) | |
663 | return (EINVAL); | |
664 | find_space = FALSE; | |
665 | (void) vm_deallocate(user_map, user_addr, user_size); | |
666 | } | |
667 | docow = FALSE; | |
668 | ||
669 | kret = vm_map_64(user_map, &user_addr, user_size, | |
670 | 0, find_space, pinfo->pshm_memobject, file_pos, docow, | |
671 | prot, VM_PROT_DEFAULT, | |
672 | VM_INHERIT_DEFAULT); | |
673 | ||
674 | if (kret != KERN_SUCCESS) | |
675 | goto out; | |
676 | kret = vm_inherit(user_map, user_addr, user_size, | |
677 | VM_INHERIT_SHARE); | |
678 | if (kret != KERN_SUCCESS) { | |
679 | (void) vm_deallocate(user_map, user_addr, user_size); | |
680 | goto out; | |
681 | } | |
682 | pnode->mapp_addr = user_addr; | |
683 | pnode->map_size = user_size; | |
684 | pinfo->pshm_flags |= (PSHM_MAPPED | PSHM_INUSE); | |
685 | out: | |
686 | switch (kret) { | |
687 | case KERN_SUCCESS: | |
688 | *fdflags(p, fd) |= UF_MAPPED; | |
689 | *retval = (register_t)(user_addr + pageoff); | |
690 | return (0); | |
691 | case KERN_INVALID_ADDRESS: | |
692 | case KERN_NO_SPACE: | |
693 | return (ENOMEM); | |
694 | case KERN_PROTECTION_FAILURE: | |
695 | return (EACCES); | |
696 | default: | |
697 | return (EINVAL); | |
698 | } | |
699 | ||
700 | } | |
701 | ||
702 | struct shm_unlink_args { | |
703 | const char *name; | |
704 | }; | |
705 | ||
706 | int | |
707 | shm_unlink(p, uap, retval) | |
708 | struct proc *p; | |
709 | register struct shm_unlink_args *uap; | |
710 | register_t *retval; | |
711 | { | |
712 | register struct filedesc *fdp = p->p_fd; | |
713 | register struct file *fp; | |
714 | int flags, i; | |
715 | int error=0; | |
716 | struct pshmname nd; | |
717 | struct pshminfo *pinfo; | |
718 | extern struct fileops pshmops; | |
719 | char * pnbuf; | |
720 | char * nameptr; | |
721 | char * cp; | |
722 | size_t pathlen, plen; | |
723 | int fmode, cmode ; | |
724 | int incache = 0; | |
725 | struct pshmnode * pnode = PSHMNODE_NULL; | |
726 | struct pshmcache *pcache = PSHMCACHE_NULL; | |
727 | kern_return_t kret; | |
728 | ||
729 | pinfo = PSHMINFO_NULL; | |
730 | ||
731 | MALLOC_ZONE(pnbuf, caddr_t, | |
732 | MAXPATHLEN, M_NAMEI, M_WAITOK); | |
733 | pathlen = MAXPATHLEN; | |
734 | error = copyinstr(uap->name, pnbuf, | |
735 | MAXPATHLEN, &pathlen); | |
736 | if (error) { | |
737 | goto bad; | |
738 | } | |
739 | if (pathlen > PSHMNAMLEN) { | |
740 | error = ENAMETOOLONG; | |
741 | goto bad; | |
742 | } | |
743 | ||
744 | ||
745 | #ifdef PSXSHM_NAME_RESTRICT | |
746 | nameptr = pnbuf; | |
747 | if (*nameptr == '/') { | |
748 | while (*(nameptr++) == '/') { | |
749 | plen--; | |
750 | error = EINVAL; | |
751 | goto bad; | |
752 | } | |
753 | } else { | |
754 | error = EINVAL; | |
755 | goto bad; | |
756 | } | |
757 | #endif /* PSXSHM_NAME_RESTRICT */ | |
758 | ||
759 | plen = pathlen; | |
760 | nameptr = pnbuf; | |
761 | nd.pshm_nameptr = nameptr; | |
762 | nd.pshm_namelen = plen; | |
763 | nd. pshm_hash =0; | |
764 | ||
765 | for (cp = nameptr, i=1; *cp != 0 && i <= plen; i++, cp++) { | |
766 | nd.pshm_hash += (unsigned char)*cp * i; | |
767 | } | |
768 | ||
769 | error = pshm_cache_search(&pinfo, &nd, &pcache); | |
770 | ||
771 | if (error == ENOENT) { | |
772 | error = EINVAL; | |
773 | goto bad; | |
774 | ||
775 | } | |
776 | if (!error) { | |
777 | error = EINVAL; | |
778 | goto bad; | |
779 | } else | |
780 | incache = 1; | |
781 | ||
782 | if ((pinfo->pshm_flags & (PSHM_DEFINED | PSHM_ALLOCATED))==0) { | |
783 | return (EINVAL); | |
784 | } | |
785 | ||
786 | if (pinfo->pshm_flags & PSHM_INDELETE) { | |
787 | error = 0; | |
788 | goto bad; | |
789 | } | |
790 | ||
791 | if (pinfo->pshm_memobject == NULL) { | |
792 | error = EINVAL; | |
793 | goto bad; | |
794 | } | |
795 | ||
796 | pinfo->pshm_flags |= PSHM_INDELETE; | |
797 | pinfo->pshm_usecount--; | |
798 | kret = mach_destroy_memory_entry(pinfo->pshm_memobject); | |
799 | pshm_cache_delete(pcache); | |
800 | _FREE(pcache, M_SHM); | |
801 | pinfo->pshm_flags |= PSHM_REMOVED; | |
802 | error = 0; | |
803 | bad: | |
804 | _FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI); | |
805 | return (error); | |
806 | out: | |
807 | switch (kret) { | |
808 | case KERN_INVALID_ADDRESS: | |
809 | case KERN_PROTECTION_FAILURE: | |
810 | return (EACCES); | |
811 | default: | |
812 | return (EINVAL); | |
813 | } | |
814 | } | |
815 | int | |
816 | pshm_closefile(fp, p) | |
817 | struct file *fp; | |
818 | struct proc *p; | |
819 | { | |
820 | ||
821 | return (pshm_close(((struct pshmnode *)fp->f_data), fp->f_flag, | |
822 | fp->f_cred, p)); | |
823 | } | |
824 | ||
825 | int | |
826 | pshm_close(pnode, flags, cred, p) | |
827 | register struct pshmnode *pnode; | |
828 | int flags; | |
829 | struct ucred *cred; | |
830 | struct proc *p; | |
831 | { | |
832 | int error=0; | |
833 | kern_return_t kret; | |
834 | register struct pshminfo *pinfo; | |
835 | ||
836 | if ((pinfo = pnode->pinfo) == PSHMINFO_NULL) | |
837 | return(EINVAL); | |
838 | ||
839 | if ((pinfo->pshm_flags & PSHM_ALLOCATED) != PSHM_ALLOCATED) { | |
840 | return(EINVAL); | |
841 | } | |
842 | #if DIAGNOSTIC | |
843 | if(!pinfo->pshm_usecount) { | |
844 | kprintf("negative usecount in pshm_close\n"); | |
845 | } | |
846 | #endif /* DIAGNOSTIC */ | |
847 | pinfo->pshm_usecount--; | |
848 | ||
849 | if ((pinfo->pshm_flags & PSHM_REMOVED) && !pinfo->pshm_usecount) { | |
850 | _FREE(pinfo,M_SHM); | |
851 | } | |
852 | _FREE(pnode, M_SHM); | |
853 | return (error); | |
854 | } | |
855 | int | |
856 | pshm_read(struct file *fp, struct uio *uio, struct ucred *cred) | |
857 | { | |
858 | return(EOPNOTSUPP); | |
859 | } | |
860 | int | |
861 | pshm_write(struct file *fp, struct uio *uio, struct ucred *cred) | |
862 | { | |
863 | return(EOPNOTSUPP); | |
864 | } | |
865 | int | |
866 | pshm_ioctl(struct file *fp, u_long com, caddr_t data, struct proc *p) | |
867 | { | |
868 | return(EOPNOTSUPP); | |
869 | } | |
870 | int | |
871 | pshm_select(struct file *fp, int which, struct proc *p) | |
872 | { | |
873 | return(EOPNOTSUPP); | |
874 | } |