Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /************************************************************ |
2 | ||
3 | Copyright 1989, 1998 The Open Group | |
4 | ||
5 | Permission to use, copy, modify, distribute, and sell this software and its | |
6 | documentation for any purpose is hereby granted without fee, provided that | |
7 | the above copyright notice appear in all copies and that both that | |
8 | copyright notice and this permission notice appear in supporting | |
9 | documentation. | |
10 | ||
11 | The above copyright notice and this permission notice shall be included in | |
12 | all copies or substantial portions of the Software. | |
13 | ||
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 | OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN | |
18 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
20 | ||
21 | Except as contained in this notice, the name of The Open Group shall not be | |
22 | used in advertising or otherwise to promote the sale, use or other dealings | |
23 | in this Software without prior written authorization from The Open Group. | |
24 | ||
25 | ********************************************************/ | |
26 | ||
27 | /* THIS IS NOT AN X CONSORTIUM STANDARD OR AN X PROJECT TEAM SPECIFICATION */ | |
28 | ||
29 | #define SHM | |
30 | ||
31 | #ifdef HAVE_DIX_CONFIG_H | |
32 | #include <dix-config.h> | |
33 | #endif | |
34 | ||
35 | #include <sys/types.h> | |
36 | #include <sys/ipc.h> | |
37 | #include <sys/shm.h> | |
38 | #include <unistd.h> | |
39 | #include <sys/stat.h> | |
40 | #include <fcntl.h> | |
41 | #include <X11/X.h> | |
42 | #include <X11/Xproto.h> | |
43 | #include "misc.h" | |
44 | #include "os.h" | |
45 | #include "dixstruct.h" | |
46 | #include "resource.h" | |
47 | #include "scrnintstr.h" | |
48 | #include "windowstr.h" | |
49 | #include "pixmapstr.h" | |
50 | #include "gcstruct.h" | |
51 | #include "extnsionst.h" | |
52 | #include "servermd.h" | |
53 | #include "shmint.h" | |
54 | #include "xace.h" | |
55 | #include <X11/extensions/shmproto.h> | |
56 | #include <X11/Xfuncproto.h> | |
57 | #include <sys/mman.h> | |
58 | #include "protocol-versions.h" | |
59 | #include "busfault.h" | |
60 | ||
61 | /* Needed for Solaris cross-zone shared memory extension */ | |
62 | #ifdef HAVE_SHMCTL64 | |
63 | #include <sys/ipc_impl.h> | |
64 | #define SHMSTAT(id, buf) shmctl64(id, IPC_STAT64, buf) | |
65 | #define SHMSTAT_TYPE struct shmid_ds64 | |
66 | #define SHMPERM_TYPE struct ipc_perm64 | |
67 | #define SHM_PERM(buf) buf.shmx_perm | |
68 | #define SHM_SEGSZ(buf) buf.shmx_segsz | |
69 | #define SHMPERM_UID(p) p->ipcx_uid | |
70 | #define SHMPERM_CUID(p) p->ipcx_cuid | |
71 | #define SHMPERM_GID(p) p->ipcx_gid | |
72 | #define SHMPERM_CGID(p) p->ipcx_cgid | |
73 | #define SHMPERM_MODE(p) p->ipcx_mode | |
74 | #define SHMPERM_ZONEID(p) p->ipcx_zoneid | |
75 | #else | |
76 | #define SHMSTAT(id, buf) shmctl(id, IPC_STAT, buf) | |
77 | #define SHMSTAT_TYPE struct shmid_ds | |
78 | #define SHMPERM_TYPE struct ipc_perm | |
79 | #define SHM_PERM(buf) buf.shm_perm | |
80 | #define SHM_SEGSZ(buf) buf.shm_segsz | |
81 | #define SHMPERM_UID(p) p->uid | |
82 | #define SHMPERM_CUID(p) p->cuid | |
83 | #define SHMPERM_GID(p) p->gid | |
84 | #define SHMPERM_CGID(p) p->cgid | |
85 | #define SHMPERM_MODE(p) p->mode | |
86 | #endif | |
87 | ||
88 | #ifdef PANORAMIX | |
89 | #include "panoramiX.h" | |
90 | #include "panoramiXsrv.h" | |
91 | #endif | |
92 | ||
93 | #include "extinit.h" | |
94 | ||
95 | typedef struct _ShmScrPrivateRec { | |
96 | CloseScreenProcPtr CloseScreen; | |
97 | ShmFuncsPtr shmFuncs; | |
98 | DestroyPixmapProcPtr destroyPixmap; | |
99 | } ShmScrPrivateRec; | |
100 | ||
101 | static PixmapPtr fbShmCreatePixmap(XSHM_CREATE_PIXMAP_ARGS); | |
102 | static int ShmDetachSegment(pointer /* value */ , | |
103 | XID /* shmseg */ | |
104 | ); | |
105 | static void ShmResetProc(ExtensionEntry * /* extEntry */ | |
106 | ); | |
107 | static void SShmCompletionEvent(xShmCompletionEvent * /* from */ , | |
108 | xShmCompletionEvent * /* to */ | |
109 | ); | |
110 | ||
111 | static Bool ShmDestroyPixmap(PixmapPtr pPixmap); | |
112 | ||
113 | static unsigned char ShmReqCode; | |
114 | int ShmCompletionCode; | |
115 | int BadShmSegCode; | |
116 | RESTYPE ShmSegType; | |
117 | static ShmDescPtr Shmsegs; | |
118 | static Bool sharedPixmaps; | |
119 | static DevPrivateKeyRec shmScrPrivateKeyRec; | |
120 | ||
121 | #define shmScrPrivateKey (&shmScrPrivateKeyRec) | |
122 | static DevPrivateKeyRec shmPixmapPrivateKeyRec; | |
123 | ||
124 | #define shmPixmapPrivateKey (&shmPixmapPrivateKeyRec) | |
125 | static ShmFuncs miFuncs = { NULL, NULL }; | |
126 | static ShmFuncs fbFuncs = { fbShmCreatePixmap, NULL }; | |
127 | ||
128 | #define ShmGetScreenPriv(s) ((ShmScrPrivateRec *)dixLookupPrivate(&(s)->devPrivates, shmScrPrivateKey)) | |
129 | ||
130 | #define VERIFY_SHMSEG(shmseg,shmdesc,client) \ | |
131 | { \ | |
132 | int tmprc; \ | |
133 | tmprc = dixLookupResourceByType((pointer *)&(shmdesc), shmseg, ShmSegType, \ | |
134 | client, DixReadAccess); \ | |
135 | if (tmprc != Success) \ | |
136 | return tmprc; \ | |
137 | } | |
138 | ||
139 | #define VERIFY_SHMPTR(shmseg,offset,needwrite,shmdesc,client) \ | |
140 | { \ | |
141 | VERIFY_SHMSEG(shmseg, shmdesc, client); \ | |
142 | if ((offset & 3) || (offset > shmdesc->size)) \ | |
143 | { \ | |
144 | client->errorValue = offset; \ | |
145 | return BadValue; \ | |
146 | } \ | |
147 | if (needwrite && !shmdesc->writable) \ | |
148 | return BadAccess; \ | |
149 | } | |
150 | ||
151 | #define VERIFY_SHMSIZE(shmdesc,offset,len,client) \ | |
152 | { \ | |
153 | if ((offset + len) > shmdesc->size) \ | |
154 | { \ | |
155 | return BadAccess; \ | |
156 | } \ | |
157 | } | |
158 | ||
159 | #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) || defined(__DragonFly__) | |
160 | #include <sys/signal.h> | |
161 | ||
162 | static Bool badSysCall = FALSE; | |
163 | ||
164 | static void | |
165 | SigSysHandler(int signo) | |
166 | { | |
167 | badSysCall = TRUE; | |
168 | } | |
169 | ||
170 | static Bool | |
171 | CheckForShmSyscall(void) | |
172 | { | |
173 | void (*oldHandler) (int); | |
174 | int shmid = -1; | |
175 | ||
176 | /* If no SHM support in the kernel, the bad syscall will generate SIGSYS */ | |
177 | oldHandler = signal(SIGSYS, SigSysHandler); | |
178 | ||
179 | badSysCall = FALSE; | |
180 | shmid = shmget(IPC_PRIVATE, 4096, IPC_CREAT); | |
181 | ||
182 | if (shmid != -1) { | |
183 | /* Successful allocation - clean up */ | |
184 | shmctl(shmid, IPC_RMID, NULL); | |
185 | } | |
186 | else { | |
187 | /* Allocation failed */ | |
188 | badSysCall = TRUE; | |
189 | } | |
190 | signal(SIGSYS, oldHandler); | |
191 | return !badSysCall; | |
192 | } | |
193 | ||
194 | #define MUST_CHECK_FOR_SHM_SYSCALL | |
195 | ||
196 | #endif | |
197 | ||
198 | static Bool | |
199 | ShmCloseScreen(ScreenPtr pScreen) | |
200 | { | |
201 | ShmScrPrivateRec *screen_priv = ShmGetScreenPriv(pScreen); | |
202 | ||
203 | pScreen->CloseScreen = screen_priv->CloseScreen; | |
204 | dixSetPrivate(&pScreen->devPrivates, shmScrPrivateKey, NULL); | |
205 | free(screen_priv); | |
206 | return (*pScreen->CloseScreen) (pScreen); | |
207 | } | |
208 | ||
209 | static ShmScrPrivateRec * | |
210 | ShmInitScreenPriv(ScreenPtr pScreen) | |
211 | { | |
212 | ShmScrPrivateRec *screen_priv = ShmGetScreenPriv(pScreen); | |
213 | ||
214 | if (!screen_priv) { | |
215 | screen_priv = calloc(1, sizeof(ShmScrPrivateRec)); | |
216 | screen_priv->CloseScreen = pScreen->CloseScreen; | |
217 | dixSetPrivate(&pScreen->devPrivates, shmScrPrivateKey, screen_priv); | |
218 | pScreen->CloseScreen = ShmCloseScreen; | |
219 | } | |
220 | return screen_priv; | |
221 | } | |
222 | ||
223 | static Bool | |
224 | ShmRegisterPrivates(void) | |
225 | { | |
226 | if (!dixRegisterPrivateKey(&shmScrPrivateKeyRec, PRIVATE_SCREEN, 0)) | |
227 | return FALSE; | |
228 | if (!dixRegisterPrivateKey(&shmPixmapPrivateKeyRec, PRIVATE_PIXMAP, 0)) | |
229 | return FALSE; | |
230 | return TRUE; | |
231 | } | |
232 | ||
233 | /*ARGSUSED*/ static void | |
234 | ShmResetProc(ExtensionEntry * extEntry) | |
235 | { | |
236 | int i; | |
237 | ||
238 | for (i = 0; i < screenInfo.numScreens; i++) | |
239 | ShmRegisterFuncs(screenInfo.screens[i], NULL); | |
240 | } | |
241 | ||
242 | void | |
243 | ShmRegisterFuncs(ScreenPtr pScreen, ShmFuncsPtr funcs) | |
244 | { | |
245 | if (!ShmRegisterPrivates()) | |
246 | return; | |
247 | ShmInitScreenPriv(pScreen)->shmFuncs = funcs; | |
248 | } | |
249 | ||
250 | static Bool | |
251 | ShmDestroyPixmap(PixmapPtr pPixmap) | |
252 | { | |
253 | ScreenPtr pScreen = pPixmap->drawable.pScreen; | |
254 | ShmScrPrivateRec *screen_priv = ShmGetScreenPriv(pScreen); | |
255 | Bool ret; | |
256 | ||
257 | if (pPixmap->refcnt == 1) { | |
258 | ShmDescPtr shmdesc; | |
259 | ||
260 | shmdesc = (ShmDescPtr) dixLookupPrivate(&pPixmap->devPrivates, | |
261 | shmPixmapPrivateKey); | |
262 | if (shmdesc) | |
263 | ShmDetachSegment((pointer) shmdesc, pPixmap->drawable.id); | |
264 | } | |
265 | ||
266 | pScreen->DestroyPixmap = screen_priv->destroyPixmap; | |
267 | ret = (*pScreen->DestroyPixmap) (pPixmap); | |
268 | screen_priv->destroyPixmap = pScreen->DestroyPixmap; | |
269 | pScreen->DestroyPixmap = ShmDestroyPixmap; | |
270 | return ret; | |
271 | } | |
272 | ||
273 | void | |
274 | ShmRegisterFbFuncs(ScreenPtr pScreen) | |
275 | { | |
276 | ShmRegisterFuncs(pScreen, &fbFuncs); | |
277 | } | |
278 | ||
279 | static int | |
280 | ProcShmQueryVersion(ClientPtr client) | |
281 | { | |
282 | xShmQueryVersionReply rep = { | |
283 | .type = X_Reply, | |
284 | .sharedPixmaps = sharedPixmaps, | |
285 | .sequenceNumber = client->sequence, | |
286 | .length = 0, | |
287 | .majorVersion = SERVER_SHM_MAJOR_VERSION, | |
288 | .minorVersion = SERVER_SHM_MINOR_VERSION, | |
289 | .uid = geteuid(), | |
290 | .gid = getegid(), | |
291 | .pixmapFormat = sharedPixmaps ? ZPixmap : 0 | |
292 | }; | |
293 | ||
294 | REQUEST_SIZE_MATCH(xShmQueryVersionReq); | |
295 | ||
296 | if (client->swapped) { | |
297 | swaps(&rep.sequenceNumber); | |
298 | swapl(&rep.length); | |
299 | swaps(&rep.majorVersion); | |
300 | swaps(&rep.minorVersion); | |
301 | swaps(&rep.uid); | |
302 | swaps(&rep.gid); | |
303 | } | |
304 | WriteToClient(client, sizeof(xShmQueryVersionReply), &rep); | |
305 | return Success; | |
306 | } | |
307 | ||
308 | /* | |
309 | * Simulate the access() system call for a shared memory segement, | |
310 | * using the credentials from the client if available | |
311 | */ | |
312 | static int | |
313 | shm_access(ClientPtr client, SHMPERM_TYPE * perm, int readonly) | |
314 | { | |
315 | int uid, gid; | |
316 | mode_t mask; | |
317 | int uidset = 0, gidset = 0; | |
318 | LocalClientCredRec *lcc; | |
319 | ||
320 | if (GetLocalClientCreds(client, &lcc) != -1) { | |
321 | ||
322 | if (lcc->fieldsSet & LCC_UID_SET) { | |
323 | uid = lcc->euid; | |
324 | uidset = 1; | |
325 | } | |
326 | if (lcc->fieldsSet & LCC_GID_SET) { | |
327 | gid = lcc->egid; | |
328 | gidset = 1; | |
329 | } | |
330 | ||
331 | #if defined(HAVE_GETZONEID) && defined(SHMPERM_ZONEID) | |
332 | if (((lcc->fieldsSet & LCC_ZID_SET) == 0) || (lcc->zoneid == -1) | |
333 | || (lcc->zoneid != SHMPERM_ZONEID(perm))) { | |
334 | uidset = 0; | |
335 | gidset = 0; | |
336 | } | |
337 | #endif | |
338 | FreeLocalClientCreds(lcc); | |
339 | ||
340 | if (uidset) { | |
341 | /* User id 0 always gets access */ | |
342 | if (uid == 0) { | |
343 | return 0; | |
344 | } | |
345 | /* Check the owner */ | |
346 | if (SHMPERM_UID(perm) == uid || SHMPERM_CUID(perm) == uid) { | |
347 | mask = S_IRUSR; | |
348 | if (!readonly) { | |
349 | mask |= S_IWUSR; | |
350 | } | |
351 | return (SHMPERM_MODE(perm) & mask) == mask ? 0 : -1; | |
352 | } | |
353 | } | |
354 | ||
355 | if (gidset) { | |
356 | /* Check the group */ | |
357 | if (SHMPERM_GID(perm) == gid || SHMPERM_CGID(perm) == gid) { | |
358 | mask = S_IRGRP; | |
359 | if (!readonly) { | |
360 | mask |= S_IWGRP; | |
361 | } | |
362 | return (SHMPERM_MODE(perm) & mask) == mask ? 0 : -1; | |
363 | } | |
364 | } | |
365 | } | |
366 | /* Otherwise, check everyone else */ | |
367 | mask = S_IROTH; | |
368 | if (!readonly) { | |
369 | mask |= S_IWOTH; | |
370 | } | |
371 | return (SHMPERM_MODE(perm) & mask) == mask ? 0 : -1; | |
372 | } | |
373 | ||
374 | static int | |
375 | ProcShmAttach(ClientPtr client) | |
376 | { | |
377 | SHMSTAT_TYPE buf; | |
378 | ShmDescPtr shmdesc; | |
379 | ||
380 | REQUEST(xShmAttachReq); | |
381 | ||
382 | REQUEST_SIZE_MATCH(xShmAttachReq); | |
383 | LEGAL_NEW_RESOURCE(stuff->shmseg, client); | |
384 | if ((stuff->readOnly != xTrue) && (stuff->readOnly != xFalse)) { | |
385 | client->errorValue = stuff->readOnly; | |
386 | return BadValue; | |
387 | } | |
388 | for (shmdesc = Shmsegs; shmdesc; shmdesc = shmdesc->next) { | |
389 | if (!SHMDESC_IS_FD(shmdesc) && shmdesc->shmid == stuff->shmid) | |
390 | break; | |
391 | } | |
392 | if (shmdesc) { | |
393 | if (!stuff->readOnly && !shmdesc->writable) | |
394 | return BadAccess; | |
395 | shmdesc->refcnt++; | |
396 | } | |
397 | else { | |
398 | shmdesc = malloc(sizeof(ShmDescRec)); | |
399 | if (!shmdesc) | |
400 | return BadAlloc; | |
401 | #ifdef SHM_FD_PASSING | |
402 | shmdesc->is_fd = FALSE; | |
403 | #endif | |
404 | shmdesc->addr = shmat(stuff->shmid, 0, | |
405 | stuff->readOnly ? SHM_RDONLY : 0); | |
406 | if ((shmdesc->addr == ((char *) -1)) || SHMSTAT(stuff->shmid, &buf)) { | |
407 | free(shmdesc); | |
408 | return BadAccess; | |
409 | } | |
410 | ||
411 | /* The attach was performed with root privs. We must | |
412 | * do manual checking of access rights for the credentials | |
413 | * of the client */ | |
414 | ||
415 | if (shm_access(client, &(SHM_PERM(buf)), stuff->readOnly) == -1) { | |
416 | shmdt(shmdesc->addr); | |
417 | free(shmdesc); | |
418 | return BadAccess; | |
419 | } | |
420 | ||
421 | shmdesc->shmid = stuff->shmid; | |
422 | shmdesc->refcnt = 1; | |
423 | shmdesc->writable = !stuff->readOnly; | |
424 | shmdesc->size = SHM_SEGSZ(buf); | |
425 | shmdesc->next = Shmsegs; | |
426 | Shmsegs = shmdesc; | |
427 | } | |
428 | if (!AddResource(stuff->shmseg, ShmSegType, (pointer) shmdesc)) | |
429 | return BadAlloc; | |
430 | return Success; | |
431 | } | |
432 | ||
433 | /*ARGSUSED*/ static int | |
434 | ShmDetachSegment(pointer value, /* must conform to DeleteType */ | |
435 | XID shmseg) | |
436 | { | |
437 | ShmDescPtr shmdesc = (ShmDescPtr) value; | |
438 | ShmDescPtr *prev; | |
439 | ||
440 | if (--shmdesc->refcnt) | |
441 | return TRUE; | |
442 | #if SHM_FD_PASSING | |
443 | if (shmdesc->is_fd) { | |
444 | if (shmdesc->busfault) | |
445 | busfault_unregister(shmdesc->busfault); | |
446 | munmap(shmdesc->addr, shmdesc->size); | |
447 | } else | |
448 | #endif | |
449 | shmdt(shmdesc->addr); | |
450 | for (prev = &Shmsegs; *prev != shmdesc; prev = &(*prev)->next); | |
451 | *prev = shmdesc->next; | |
452 | free(shmdesc); | |
453 | return Success; | |
454 | } | |
455 | ||
456 | static int | |
457 | ProcShmDetach(ClientPtr client) | |
458 | { | |
459 | ShmDescPtr shmdesc; | |
460 | ||
461 | REQUEST(xShmDetachReq); | |
462 | ||
463 | REQUEST_SIZE_MATCH(xShmDetachReq); | |
464 | VERIFY_SHMSEG(stuff->shmseg, shmdesc, client); | |
465 | FreeResource(stuff->shmseg, RT_NONE); | |
466 | return Success; | |
467 | } | |
468 | ||
469 | /* | |
470 | * If the given request doesn't exactly match PutImage's constraints, | |
471 | * wrap the image in a scratch pixmap header and let CopyArea sort it out. | |
472 | */ | |
473 | static void | |
474 | doShmPutImage(DrawablePtr dst, GCPtr pGC, | |
475 | int depth, unsigned int format, | |
476 | int w, int h, int sx, int sy, int sw, int sh, int dx, int dy, | |
477 | char *data) | |
478 | { | |
479 | PixmapPtr pPixmap; | |
480 | ||
481 | if (format == ZPixmap || (format == XYPixmap && depth == 1)) { | |
482 | pPixmap = GetScratchPixmapHeader(dst->pScreen, w, h, depth, | |
483 | BitsPerPixel(depth), | |
484 | PixmapBytePad(w, depth), data); | |
485 | if (!pPixmap) | |
486 | return; | |
487 | pGC->ops->CopyArea((DrawablePtr) pPixmap, dst, pGC, sx, sy, sw, sh, dx, | |
488 | dy); | |
489 | FreeScratchPixmapHeader(pPixmap); | |
490 | } | |
491 | else { | |
492 | GCPtr putGC = GetScratchGC(depth, dst->pScreen); | |
493 | ||
494 | if (!putGC) | |
495 | return; | |
496 | ||
497 | pPixmap = (*dst->pScreen->CreatePixmap) (dst->pScreen, sw, sh, depth, | |
498 | CREATE_PIXMAP_USAGE_SCRATCH); | |
499 | if (!pPixmap) { | |
500 | FreeScratchGC(putGC); | |
501 | return; | |
502 | } | |
503 | ValidateGC(&pPixmap->drawable, putGC); | |
504 | (*putGC->ops->PutImage) (&pPixmap->drawable, putGC, depth, -sx, -sy, w, | |
505 | h, 0, | |
506 | (format == XYPixmap) ? XYPixmap : ZPixmap, | |
507 | data); | |
508 | FreeScratchGC(putGC); | |
509 | if (format == XYBitmap) | |
510 | (void) (*pGC->ops->CopyPlane) (&pPixmap->drawable, dst, pGC, 0, 0, | |
511 | sw, sh, dx, dy, 1L); | |
512 | else | |
513 | (void) (*pGC->ops->CopyArea) (&pPixmap->drawable, dst, pGC, 0, 0, | |
514 | sw, sh, dx, dy); | |
515 | (*pPixmap->drawable.pScreen->DestroyPixmap) (pPixmap); | |
516 | } | |
517 | } | |
518 | ||
519 | static int | |
520 | ProcShmPutImage(ClientPtr client) | |
521 | { | |
522 | GCPtr pGC; | |
523 | DrawablePtr pDraw; | |
524 | long length; | |
525 | ShmDescPtr shmdesc; | |
526 | ||
527 | REQUEST(xShmPutImageReq); | |
528 | ||
529 | REQUEST_SIZE_MATCH(xShmPutImageReq); | |
530 | VALIDATE_DRAWABLE_AND_GC(stuff->drawable, pDraw, DixWriteAccess); | |
531 | VERIFY_SHMPTR(stuff->shmseg, stuff->offset, FALSE, shmdesc, client); | |
532 | if ((stuff->sendEvent != xTrue) && (stuff->sendEvent != xFalse)) | |
533 | return BadValue; | |
534 | if (stuff->format == XYBitmap) { | |
535 | if (stuff->depth != 1) | |
536 | return BadMatch; | |
537 | length = PixmapBytePad(stuff->totalWidth, 1); | |
538 | } | |
539 | else if (stuff->format == XYPixmap) { | |
540 | if (pDraw->depth != stuff->depth) | |
541 | return BadMatch; | |
542 | length = PixmapBytePad(stuff->totalWidth, 1); | |
543 | length *= stuff->depth; | |
544 | } | |
545 | else if (stuff->format == ZPixmap) { | |
546 | if (pDraw->depth != stuff->depth) | |
547 | return BadMatch; | |
548 | length = PixmapBytePad(stuff->totalWidth, stuff->depth); | |
549 | } | |
550 | else { | |
551 | client->errorValue = stuff->format; | |
552 | return BadValue; | |
553 | } | |
554 | ||
555 | /* | |
556 | * There's a potential integer overflow in this check: | |
557 | * VERIFY_SHMSIZE(shmdesc, stuff->offset, length * stuff->totalHeight, | |
558 | * client); | |
559 | * the version below ought to avoid it | |
560 | */ | |
561 | if (stuff->totalHeight != 0 && | |
562 | length > (shmdesc->size - stuff->offset) / stuff->totalHeight) { | |
563 | client->errorValue = stuff->totalWidth; | |
564 | return BadValue; | |
565 | } | |
566 | if (stuff->srcX > stuff->totalWidth) { | |
567 | client->errorValue = stuff->srcX; | |
568 | return BadValue; | |
569 | } | |
570 | if (stuff->srcY > stuff->totalHeight) { | |
571 | client->errorValue = stuff->srcY; | |
572 | return BadValue; | |
573 | } | |
574 | if ((stuff->srcX + stuff->srcWidth) > stuff->totalWidth) { | |
575 | client->errorValue = stuff->srcWidth; | |
576 | return BadValue; | |
577 | } | |
578 | if ((stuff->srcY + stuff->srcHeight) > stuff->totalHeight) { | |
579 | client->errorValue = stuff->srcHeight; | |
580 | return BadValue; | |
581 | } | |
582 | ||
583 | if ((((stuff->format == ZPixmap) && (stuff->srcX == 0)) || | |
584 | ((stuff->format != ZPixmap) && | |
585 | (stuff->srcX < screenInfo.bitmapScanlinePad) && | |
586 | ((stuff->format == XYBitmap) || | |
587 | ((stuff->srcY == 0) && | |
588 | (stuff->srcHeight == stuff->totalHeight))))) && | |
589 | ((stuff->srcX + stuff->srcWidth) == stuff->totalWidth)) | |
590 | (*pGC->ops->PutImage) (pDraw, pGC, stuff->depth, | |
591 | stuff->dstX, stuff->dstY, | |
592 | stuff->totalWidth, stuff->srcHeight, | |
593 | stuff->srcX, stuff->format, | |
594 | shmdesc->addr + stuff->offset + | |
595 | (stuff->srcY * length)); | |
596 | else | |
597 | doShmPutImage(pDraw, pGC, stuff->depth, stuff->format, | |
598 | stuff->totalWidth, stuff->totalHeight, | |
599 | stuff->srcX, stuff->srcY, | |
600 | stuff->srcWidth, stuff->srcHeight, | |
601 | stuff->dstX, stuff->dstY, shmdesc->addr + stuff->offset); | |
602 | ||
603 | if (stuff->sendEvent) { | |
604 | xShmCompletionEvent ev = { | |
605 | .type = ShmCompletionCode, | |
606 | .drawable = stuff->drawable, | |
607 | .minorEvent = X_ShmPutImage, | |
608 | .majorEvent = ShmReqCode, | |
609 | .shmseg = stuff->shmseg, | |
610 | .offset = stuff->offset | |
611 | }; | |
612 | WriteEventsToClient(client, 1, (xEvent *) &ev); | |
613 | } | |
614 | ||
615 | return Success; | |
616 | } | |
617 | ||
618 | static int | |
619 | ProcShmGetImage(ClientPtr client) | |
620 | { | |
621 | DrawablePtr pDraw; | |
622 | long lenPer = 0, length; | |
623 | Mask plane = 0; | |
624 | xShmGetImageReply xgi; | |
625 | ShmDescPtr shmdesc; | |
626 | VisualID visual = None; | |
627 | int rc; | |
628 | ||
629 | REQUEST(xShmGetImageReq); | |
630 | ||
631 | REQUEST_SIZE_MATCH(xShmGetImageReq); | |
632 | if ((stuff->format != XYPixmap) && (stuff->format != ZPixmap)) { | |
633 | client->errorValue = stuff->format; | |
634 | return BadValue; | |
635 | } | |
636 | rc = dixLookupDrawable(&pDraw, stuff->drawable, client, 0, DixReadAccess); | |
637 | if (rc != Success) | |
638 | return rc; | |
639 | VERIFY_SHMPTR(stuff->shmseg, stuff->offset, TRUE, shmdesc, client); | |
640 | if (pDraw->type == DRAWABLE_WINDOW) { | |
641 | if ( /* check for being viewable */ | |
642 | !((WindowPtr) pDraw)->realized || | |
643 | /* check for being on screen */ | |
644 | pDraw->x + stuff->x < 0 || | |
645 | pDraw->x + stuff->x + (int) stuff->width > pDraw->pScreen->width | |
646 | || pDraw->y + stuff->y < 0 || | |
647 | pDraw->y + stuff->y + (int) stuff->height > | |
648 | pDraw->pScreen->height || | |
649 | /* check for being inside of border */ | |
650 | stuff->x < -wBorderWidth((WindowPtr) pDraw) || | |
651 | stuff->x + (int) stuff->width > | |
652 | wBorderWidth((WindowPtr) pDraw) + (int) pDraw->width || | |
653 | stuff->y < -wBorderWidth((WindowPtr) pDraw) || | |
654 | stuff->y + (int) stuff->height > | |
655 | wBorderWidth((WindowPtr) pDraw) + (int) pDraw->height) | |
656 | return BadMatch; | |
657 | visual = wVisual(((WindowPtr) pDraw)); | |
658 | } | |
659 | else { | |
660 | if (stuff->x < 0 || | |
661 | stuff->x + (int) stuff->width > pDraw->width || | |
662 | stuff->y < 0 || stuff->y + (int) stuff->height > pDraw->height) | |
663 | return BadMatch; | |
664 | visual = None; | |
665 | } | |
666 | xgi = (xShmGetImageReply) { | |
667 | .type = X_Reply, | |
668 | .sequenceNumber = client->sequence, | |
669 | .length = 0, | |
670 | .visual = visual, | |
671 | .depth = pDraw->depth | |
672 | }; | |
673 | if (stuff->format == ZPixmap) { | |
674 | length = PixmapBytePad(stuff->width, pDraw->depth) * stuff->height; | |
675 | } | |
676 | else { | |
677 | lenPer = PixmapBytePad(stuff->width, 1) * stuff->height; | |
678 | plane = ((Mask) 1) << (pDraw->depth - 1); | |
679 | /* only planes asked for */ | |
680 | length = lenPer * Ones(stuff->planeMask & (plane | (plane - 1))); | |
681 | } | |
682 | ||
683 | VERIFY_SHMSIZE(shmdesc, stuff->offset, length, client); | |
684 | xgi.size = length; | |
685 | ||
686 | if (length == 0) { | |
687 | /* nothing to do */ | |
688 | } | |
689 | else if (stuff->format == ZPixmap) { | |
690 | (*pDraw->pScreen->GetImage) (pDraw, stuff->x, stuff->y, | |
691 | stuff->width, stuff->height, | |
692 | stuff->format, stuff->planeMask, | |
693 | shmdesc->addr + stuff->offset); | |
694 | } | |
695 | else { | |
696 | ||
697 | length = stuff->offset; | |
698 | for (; plane; plane >>= 1) { | |
699 | if (stuff->planeMask & plane) { | |
700 | (*pDraw->pScreen->GetImage) (pDraw, | |
701 | stuff->x, stuff->y, | |
702 | stuff->width, stuff->height, | |
703 | stuff->format, plane, | |
704 | shmdesc->addr + length); | |
705 | length += lenPer; | |
706 | } | |
707 | } | |
708 | } | |
709 | ||
710 | if (client->swapped) { | |
711 | swaps(&xgi.sequenceNumber); | |
712 | swapl(&xgi.length); | |
713 | swapl(&xgi.visual); | |
714 | swapl(&xgi.size); | |
715 | } | |
716 | WriteToClient(client, sizeof(xShmGetImageReply), &xgi); | |
717 | ||
718 | return Success; | |
719 | } | |
720 | ||
721 | #ifdef PANORAMIX | |
722 | static int | |
723 | ProcPanoramiXShmPutImage(ClientPtr client) | |
724 | { | |
725 | int j, result, orig_x, orig_y; | |
726 | PanoramiXRes *draw, *gc; | |
727 | Bool sendEvent, isRoot; | |
728 | ||
729 | REQUEST(xShmPutImageReq); | |
730 | REQUEST_SIZE_MATCH(xShmPutImageReq); | |
731 | ||
732 | result = dixLookupResourceByClass((pointer *) &draw, stuff->drawable, | |
733 | XRC_DRAWABLE, client, DixWriteAccess); | |
734 | if (result != Success) | |
735 | return (result == BadValue) ? BadDrawable : result; | |
736 | ||
737 | result = dixLookupResourceByType((pointer *) &gc, stuff->gc, | |
738 | XRT_GC, client, DixReadAccess); | |
739 | if (result != Success) | |
740 | return result; | |
741 | ||
742 | isRoot = (draw->type == XRT_WINDOW) && draw->u.win.root; | |
743 | ||
744 | orig_x = stuff->dstX; | |
745 | orig_y = stuff->dstY; | |
746 | sendEvent = stuff->sendEvent; | |
747 | stuff->sendEvent = 0; | |
748 | FOR_NSCREENS(j) { | |
749 | if (!j) | |
750 | stuff->sendEvent = sendEvent; | |
751 | stuff->drawable = draw->info[j].id; | |
752 | stuff->gc = gc->info[j].id; | |
753 | if (isRoot) { | |
754 | stuff->dstX = orig_x - screenInfo.screens[j]->x; | |
755 | stuff->dstY = orig_y - screenInfo.screens[j]->y; | |
756 | } | |
757 | result = ProcShmPutImage(client); | |
758 | if (result != Success) | |
759 | break; | |
760 | } | |
761 | return result; | |
762 | } | |
763 | ||
764 | static int | |
765 | ProcPanoramiXShmGetImage(ClientPtr client) | |
766 | { | |
767 | PanoramiXRes *draw; | |
768 | DrawablePtr *drawables; | |
769 | DrawablePtr pDraw; | |
770 | xShmGetImageReply xgi; | |
771 | ShmDescPtr shmdesc; | |
772 | int i, x, y, w, h, format, rc; | |
773 | Mask plane = 0, planemask; | |
774 | long lenPer = 0, length, widthBytesLine; | |
775 | Bool isRoot; | |
776 | ||
777 | REQUEST(xShmGetImageReq); | |
778 | ||
779 | REQUEST_SIZE_MATCH(xShmGetImageReq); | |
780 | ||
781 | if ((stuff->format != XYPixmap) && (stuff->format != ZPixmap)) { | |
782 | client->errorValue = stuff->format; | |
783 | return BadValue; | |
784 | } | |
785 | ||
786 | rc = dixLookupResourceByClass((pointer *) &draw, stuff->drawable, | |
787 | XRC_DRAWABLE, client, DixWriteAccess); | |
788 | if (rc != Success) | |
789 | return (rc == BadValue) ? BadDrawable : rc; | |
790 | ||
791 | if (draw->type == XRT_PIXMAP) | |
792 | return ProcShmGetImage(client); | |
793 | ||
794 | rc = dixLookupDrawable(&pDraw, stuff->drawable, client, 0, DixReadAccess); | |
795 | if (rc != Success) | |
796 | return rc; | |
797 | ||
798 | VERIFY_SHMPTR(stuff->shmseg, stuff->offset, TRUE, shmdesc, client); | |
799 | ||
800 | x = stuff->x; | |
801 | y = stuff->y; | |
802 | w = stuff->width; | |
803 | h = stuff->height; | |
804 | format = stuff->format; | |
805 | planemask = stuff->planeMask; | |
806 | ||
807 | isRoot = (draw->type == XRT_WINDOW) && draw->u.win.root; | |
808 | ||
809 | if (isRoot) { | |
810 | if ( /* check for being onscreen */ | |
811 | x < 0 || x + w > PanoramiXPixWidth || | |
812 | y < 0 || y + h > PanoramiXPixHeight) | |
813 | return BadMatch; | |
814 | } | |
815 | else { | |
816 | if ( /* check for being onscreen */ | |
817 | screenInfo.screens[0]->x + pDraw->x + x < 0 || | |
818 | screenInfo.screens[0]->x + pDraw->x + x + w > PanoramiXPixWidth | |
819 | || screenInfo.screens[0]->y + pDraw->y + y < 0 || | |
820 | screenInfo.screens[0]->y + pDraw->y + y + h > PanoramiXPixHeight | |
821 | || | |
822 | /* check for being inside of border */ | |
823 | x < -wBorderWidth((WindowPtr) pDraw) || | |
824 | x + w > wBorderWidth((WindowPtr) pDraw) + (int) pDraw->width || | |
825 | y < -wBorderWidth((WindowPtr) pDraw) || | |
826 | y + h > wBorderWidth((WindowPtr) pDraw) + (int) pDraw->height) | |
827 | return BadMatch; | |
828 | } | |
829 | ||
830 | drawables = calloc(PanoramiXNumScreens, sizeof(DrawablePtr)); | |
831 | if (!drawables) | |
832 | return BadAlloc; | |
833 | ||
834 | drawables[0] = pDraw; | |
835 | FOR_NSCREENS_FORWARD_SKIP(i) { | |
836 | rc = dixLookupDrawable(drawables + i, draw->info[i].id, client, 0, | |
837 | DixReadAccess); | |
838 | if (rc != Success) { | |
839 | free(drawables); | |
840 | return rc; | |
841 | } | |
842 | } | |
843 | ||
844 | xgi = (xShmGetImageReply) { | |
845 | .type = X_Reply, | |
846 | .sequenceNumber = client->sequence, | |
847 | .length = 0, | |
848 | .visual = wVisual(((WindowPtr) pDraw)), | |
849 | .depth = pDraw->depth | |
850 | }; | |
851 | ||
852 | if (format == ZPixmap) { | |
853 | widthBytesLine = PixmapBytePad(w, pDraw->depth); | |
854 | length = widthBytesLine * h; | |
855 | } | |
856 | else { | |
857 | widthBytesLine = PixmapBytePad(w, 1); | |
858 | lenPer = widthBytesLine * h; | |
859 | plane = ((Mask) 1) << (pDraw->depth - 1); | |
860 | length = lenPer * Ones(planemask & (plane | (plane - 1))); | |
861 | } | |
862 | ||
863 | VERIFY_SHMSIZE(shmdesc, stuff->offset, length, client); | |
864 | xgi.size = length; | |
865 | ||
866 | if (length == 0) { /* nothing to do */ | |
867 | } | |
868 | else if (format == ZPixmap) { | |
869 | XineramaGetImageData(drawables, x, y, w, h, format, planemask, | |
870 | shmdesc->addr + stuff->offset, | |
871 | widthBytesLine, isRoot); | |
872 | } | |
873 | else { | |
874 | ||
875 | length = stuff->offset; | |
876 | for (; plane; plane >>= 1) { | |
877 | if (planemask & plane) { | |
878 | XineramaGetImageData(drawables, x, y, w, h, | |
879 | format, plane, shmdesc->addr + length, | |
880 | widthBytesLine, isRoot); | |
881 | length += lenPer; | |
882 | } | |
883 | } | |
884 | } | |
885 | free(drawables); | |
886 | ||
887 | if (client->swapped) { | |
888 | swaps(&xgi.sequenceNumber); | |
889 | swapl(&xgi.length); | |
890 | swapl(&xgi.visual); | |
891 | swapl(&xgi.size); | |
892 | } | |
893 | WriteToClient(client, sizeof(xShmGetImageReply), &xgi); | |
894 | ||
895 | return Success; | |
896 | } | |
897 | ||
898 | static int | |
899 | ProcPanoramiXShmCreatePixmap(ClientPtr client) | |
900 | { | |
901 | ScreenPtr pScreen = NULL; | |
902 | PixmapPtr pMap = NULL; | |
903 | DrawablePtr pDraw; | |
904 | DepthPtr pDepth; | |
905 | int i, j, result, rc; | |
906 | ShmDescPtr shmdesc; | |
907 | ||
908 | REQUEST(xShmCreatePixmapReq); | |
909 | unsigned int width, height, depth; | |
910 | unsigned long size; | |
911 | PanoramiXRes *newPix; | |
912 | ||
913 | REQUEST_SIZE_MATCH(xShmCreatePixmapReq); | |
914 | client->errorValue = stuff->pid; | |
915 | if (!sharedPixmaps) | |
916 | return BadImplementation; | |
917 | LEGAL_NEW_RESOURCE(stuff->pid, client); | |
918 | rc = dixLookupDrawable(&pDraw, stuff->drawable, client, M_ANY, | |
919 | DixGetAttrAccess); | |
920 | if (rc != Success) | |
921 | return rc; | |
922 | ||
923 | VERIFY_SHMPTR(stuff->shmseg, stuff->offset, TRUE, shmdesc, client); | |
924 | ||
925 | width = stuff->width; | |
926 | height = stuff->height; | |
927 | depth = stuff->depth; | |
928 | if (!width || !height || !depth) { | |
929 | client->errorValue = 0; | |
930 | return BadValue; | |
931 | } | |
932 | if (width > 32767 || height > 32767) | |
933 | return BadAlloc; | |
934 | ||
935 | if (stuff->depth != 1) { | |
936 | pDepth = pDraw->pScreen->allowedDepths; | |
937 | for (i = 0; i < pDraw->pScreen->numDepths; i++, pDepth++) | |
938 | if (pDepth->depth == stuff->depth) | |
939 | goto CreatePmap; | |
940 | client->errorValue = stuff->depth; | |
941 | return BadValue; | |
942 | } | |
943 | ||
944 | CreatePmap: | |
945 | size = PixmapBytePad(width, depth) * height; | |
946 | if (sizeof(size) == 4 && BitsPerPixel(depth) > 8) { | |
947 | if (size < width * height) | |
948 | return BadAlloc; | |
949 | } | |
950 | /* thankfully, offset is unsigned */ | |
951 | if (stuff->offset + size < size) | |
952 | return BadAlloc; | |
953 | ||
954 | VERIFY_SHMSIZE(shmdesc, stuff->offset, size, client); | |
955 | ||
956 | if (!(newPix = malloc(sizeof(PanoramiXRes)))) | |
957 | return BadAlloc; | |
958 | ||
959 | newPix->type = XRT_PIXMAP; | |
960 | newPix->u.pix.shared = TRUE; | |
961 | panoramix_setup_ids(newPix, client, stuff->pid); | |
962 | ||
963 | result = Success; | |
964 | ||
965 | FOR_NSCREENS(j) { | |
966 | ShmScrPrivateRec *screen_priv; | |
967 | ||
968 | pScreen = screenInfo.screens[j]; | |
969 | ||
970 | screen_priv = ShmGetScreenPriv(pScreen); | |
971 | pMap = (*screen_priv->shmFuncs->CreatePixmap) (pScreen, | |
972 | stuff->width, | |
973 | stuff->height, | |
974 | stuff->depth, | |
975 | shmdesc->addr + | |
976 | stuff->offset); | |
977 | ||
978 | if (pMap) { | |
979 | dixSetPrivate(&pMap->devPrivates, shmPixmapPrivateKey, shmdesc); | |
980 | shmdesc->refcnt++; | |
981 | pMap->drawable.serialNumber = NEXT_SERIAL_NUMBER; | |
982 | pMap->drawable.id = newPix->info[j].id; | |
983 | if (!AddResource(newPix->info[j].id, RT_PIXMAP, (pointer) pMap)) { | |
984 | result = BadAlloc; | |
985 | break; | |
986 | } | |
987 | } | |
988 | else { | |
989 | result = BadAlloc; | |
990 | break; | |
991 | } | |
992 | } | |
993 | ||
994 | if (result == BadAlloc) { | |
995 | while (j--) | |
996 | FreeResource(newPix->info[j].id, RT_NONE); | |
997 | free(newPix); | |
998 | } | |
999 | else | |
1000 | AddResource(stuff->pid, XRT_PIXMAP, newPix); | |
1001 | ||
1002 | return result; | |
1003 | } | |
1004 | #endif | |
1005 | ||
1006 | static PixmapPtr | |
1007 | fbShmCreatePixmap(ScreenPtr pScreen, | |
1008 | int width, int height, int depth, char *addr) | |
1009 | { | |
1010 | PixmapPtr pPixmap; | |
1011 | ||
1012 | pPixmap = (*pScreen->CreatePixmap) (pScreen, 0, 0, pScreen->rootDepth, 0); | |
1013 | if (!pPixmap) | |
1014 | return NullPixmap; | |
1015 | ||
1016 | if (!(*pScreen->ModifyPixmapHeader) (pPixmap, width, height, depth, | |
1017 | BitsPerPixel(depth), | |
1018 | PixmapBytePad(width, depth), | |
1019 | (pointer) addr)) { | |
1020 | (*pScreen->DestroyPixmap) (pPixmap); | |
1021 | return NullPixmap; | |
1022 | } | |
1023 | return pPixmap; | |
1024 | } | |
1025 | ||
1026 | static int | |
1027 | ProcShmCreatePixmap(ClientPtr client) | |
1028 | { | |
1029 | PixmapPtr pMap; | |
1030 | DrawablePtr pDraw; | |
1031 | DepthPtr pDepth; | |
1032 | int i, rc; | |
1033 | ShmDescPtr shmdesc; | |
1034 | ShmScrPrivateRec *screen_priv; | |
1035 | ||
1036 | REQUEST(xShmCreatePixmapReq); | |
1037 | unsigned int width, height, depth; | |
1038 | unsigned long size; | |
1039 | ||
1040 | REQUEST_SIZE_MATCH(xShmCreatePixmapReq); | |
1041 | client->errorValue = stuff->pid; | |
1042 | if (!sharedPixmaps) | |
1043 | return BadImplementation; | |
1044 | LEGAL_NEW_RESOURCE(stuff->pid, client); | |
1045 | rc = dixLookupDrawable(&pDraw, stuff->drawable, client, M_ANY, | |
1046 | DixGetAttrAccess); | |
1047 | if (rc != Success) | |
1048 | return rc; | |
1049 | ||
1050 | VERIFY_SHMPTR(stuff->shmseg, stuff->offset, TRUE, shmdesc, client); | |
1051 | ||
1052 | width = stuff->width; | |
1053 | height = stuff->height; | |
1054 | depth = stuff->depth; | |
1055 | if (!width || !height || !depth) { | |
1056 | client->errorValue = 0; | |
1057 | return BadValue; | |
1058 | } | |
1059 | if (width > 32767 || height > 32767) | |
1060 | return BadAlloc; | |
1061 | ||
1062 | if (stuff->depth != 1) { | |
1063 | pDepth = pDraw->pScreen->allowedDepths; | |
1064 | for (i = 0; i < pDraw->pScreen->numDepths; i++, pDepth++) | |
1065 | if (pDepth->depth == stuff->depth) | |
1066 | goto CreatePmap; | |
1067 | client->errorValue = stuff->depth; | |
1068 | return BadValue; | |
1069 | } | |
1070 | ||
1071 | CreatePmap: | |
1072 | size = PixmapBytePad(width, depth) * height; | |
1073 | if (sizeof(size) == 4 && BitsPerPixel(depth) > 8) { | |
1074 | if (size < width * height) | |
1075 | return BadAlloc; | |
1076 | } | |
1077 | /* thankfully, offset is unsigned */ | |
1078 | if (stuff->offset + size < size) | |
1079 | return BadAlloc; | |
1080 | ||
1081 | VERIFY_SHMSIZE(shmdesc, stuff->offset, size, client); | |
1082 | screen_priv = ShmGetScreenPriv(pDraw->pScreen); | |
1083 | pMap = (*screen_priv->shmFuncs->CreatePixmap) (pDraw->pScreen, stuff->width, | |
1084 | stuff->height, stuff->depth, | |
1085 | shmdesc->addr + | |
1086 | stuff->offset); | |
1087 | if (pMap) { | |
1088 | rc = XaceHook(XACE_RESOURCE_ACCESS, client, stuff->pid, RT_PIXMAP, | |
1089 | pMap, RT_NONE, NULL, DixCreateAccess); | |
1090 | if (rc != Success) { | |
1091 | pDraw->pScreen->DestroyPixmap(pMap); | |
1092 | return rc; | |
1093 | } | |
1094 | dixSetPrivate(&pMap->devPrivates, shmPixmapPrivateKey, shmdesc); | |
1095 | shmdesc->refcnt++; | |
1096 | pMap->drawable.serialNumber = NEXT_SERIAL_NUMBER; | |
1097 | pMap->drawable.id = stuff->pid; | |
1098 | if (AddResource(stuff->pid, RT_PIXMAP, (pointer) pMap)) { | |
1099 | return Success; | |
1100 | } | |
1101 | } | |
1102 | return BadAlloc; | |
1103 | } | |
1104 | ||
1105 | #ifdef SHM_FD_PASSING | |
1106 | ||
1107 | static void | |
1108 | ShmBusfaultNotify(void *context) | |
1109 | { | |
1110 | ShmDescPtr shmdesc = context; | |
1111 | ||
1112 | ErrorF("shared memory 0x%x truncated by client\n", | |
1113 | (unsigned int) shmdesc->resource); | |
1114 | busfault_unregister(shmdesc->busfault); | |
1115 | shmdesc->busfault = NULL; | |
1116 | FreeResource (shmdesc->resource, RT_NONE); | |
1117 | } | |
1118 | ||
1119 | static int | |
1120 | ProcShmAttachFd(ClientPtr client) | |
1121 | { | |
1122 | int fd; | |
1123 | ShmDescPtr shmdesc; | |
1124 | REQUEST(xShmAttachFdReq); | |
1125 | struct stat statb; | |
1126 | ||
1127 | SetReqFds(client, 1); | |
1128 | REQUEST_SIZE_MATCH(xShmAttachFdReq); | |
1129 | LEGAL_NEW_RESOURCE(stuff->shmseg, client); | |
1130 | if ((stuff->readOnly != xTrue) && (stuff->readOnly != xFalse)) { | |
1131 | client->errorValue = stuff->readOnly; | |
1132 | return BadValue; | |
1133 | } | |
1134 | fd = ReadFdFromClient(client); | |
1135 | if (fd < 0) | |
1136 | return BadMatch; | |
1137 | ||
1138 | if (fstat(fd, &statb) < 0 || statb.st_size == 0) { | |
1139 | close(fd); | |
1140 | return BadMatch; | |
1141 | } | |
1142 | ||
1143 | shmdesc = malloc(sizeof(ShmDescRec)); | |
1144 | if (!shmdesc) { | |
1145 | close(fd); | |
1146 | return BadAlloc; | |
1147 | } | |
1148 | shmdesc->is_fd = TRUE; | |
1149 | shmdesc->addr = mmap(NULL, statb.st_size, | |
1150 | stuff->readOnly ? PROT_READ : PROT_READ|PROT_WRITE, | |
1151 | MAP_SHARED, | |
1152 | fd, 0); | |
1153 | ||
1154 | close(fd); | |
1155 | if ((shmdesc->addr == ((char *) -1))) { | |
1156 | free(shmdesc); | |
1157 | return BadAccess; | |
1158 | } | |
1159 | ||
1160 | shmdesc->refcnt = 1; | |
1161 | shmdesc->writable = !stuff->readOnly; | |
1162 | shmdesc->size = statb.st_size; | |
1163 | shmdesc->resource = stuff->shmseg; | |
1164 | ||
1165 | shmdesc->busfault = busfault_register_mmap(shmdesc->addr, shmdesc->size, ShmBusfaultNotify, shmdesc); | |
1166 | if (!shmdesc->busfault) { | |
1167 | munmap(shmdesc->addr, shmdesc->size); | |
1168 | free(shmdesc); | |
1169 | return BadAlloc; | |
1170 | } | |
1171 | ||
1172 | shmdesc->next = Shmsegs; | |
1173 | Shmsegs = shmdesc; | |
1174 | ||
1175 | if (!AddResource(stuff->shmseg, ShmSegType, (pointer) shmdesc)) | |
1176 | return BadAlloc; | |
1177 | return Success; | |
1178 | } | |
1179 | ||
1180 | static int | |
1181 | shm_tmpfile(void) | |
1182 | { | |
1183 | #ifdef SHMDIR | |
1184 | int fd; | |
1185 | int flags; | |
1186 | char template[] = SHMDIR "/shmfd-XXXXXX"; | |
1187 | #ifdef O_TMPFILE | |
1188 | fd = open(SHMDIR, O_TMPFILE|O_RDWR|O_CLOEXEC|O_EXCL, 0666); | |
1189 | if (fd >= 0) { | |
1190 | ErrorF ("Using O_TMPFILE\n"); | |
1191 | return fd; | |
1192 | } | |
1193 | ErrorF ("Not using O_TMPFILE\n"); | |
1194 | #endif | |
1195 | fd = mkstemp(template); | |
1196 | if (fd < 0) | |
1197 | return -1; | |
1198 | unlink(template); | |
1199 | if (fcntl(fd, F_GETFD, &flags) >= 0) { | |
1200 | flags |= FD_CLOEXEC; | |
1201 | (void) fcntl(fd, F_SETFD, &flags); | |
1202 | } | |
1203 | return fd; | |
1204 | #else | |
1205 | return -1; | |
1206 | #endif | |
1207 | } | |
1208 | ||
1209 | static int | |
1210 | ProcShmCreateSegment(ClientPtr client) | |
1211 | { | |
1212 | int fd; | |
1213 | ShmDescPtr shmdesc; | |
1214 | REQUEST(xShmCreateSegmentReq); | |
1215 | xShmCreateSegmentReply rep = { | |
1216 | .type = X_Reply, | |
1217 | .nfd = 1, | |
1218 | .sequenceNumber = client->sequence, | |
1219 | .length = 0, | |
1220 | }; | |
1221 | ||
1222 | REQUEST_SIZE_MATCH(xShmCreateSegmentReq); | |
1223 | if ((stuff->readOnly != xTrue) && (stuff->readOnly != xFalse)) { | |
1224 | client->errorValue = stuff->readOnly; | |
1225 | return BadValue; | |
1226 | } | |
1227 | fd = shm_tmpfile(); | |
1228 | if (fd < 0) | |
1229 | return BadAlloc; | |
1230 | if (ftruncate(fd, stuff->size) < 0) { | |
1231 | close(fd); | |
1232 | return BadAlloc; | |
1233 | } | |
1234 | shmdesc = malloc(sizeof(ShmDescRec)); | |
1235 | if (!shmdesc) { | |
1236 | close(fd); | |
1237 | return BadAlloc; | |
1238 | } | |
1239 | shmdesc->is_fd = TRUE; | |
1240 | shmdesc->addr = mmap(NULL, stuff->size, | |
1241 | stuff->readOnly ? PROT_READ : PROT_READ|PROT_WRITE, | |
1242 | MAP_SHARED, | |
1243 | fd, 0); | |
1244 | ||
1245 | if ((shmdesc->addr == ((char *) -1))) { | |
1246 | close(fd); | |
1247 | free(shmdesc); | |
1248 | return BadAccess; | |
1249 | } | |
1250 | ||
1251 | shmdesc->refcnt = 1; | |
1252 | shmdesc->writable = !stuff->readOnly; | |
1253 | shmdesc->size = stuff->size; | |
1254 | ||
1255 | shmdesc->busfault = busfault_register_mmap(shmdesc->addr, shmdesc->size, ShmBusfaultNotify, shmdesc); | |
1256 | if (!shmdesc->busfault) { | |
1257 | close(fd); | |
1258 | munmap(shmdesc->addr, shmdesc->size); | |
1259 | free(shmdesc); | |
1260 | return BadAlloc; | |
1261 | } | |
1262 | ||
1263 | shmdesc->next = Shmsegs; | |
1264 | Shmsegs = shmdesc; | |
1265 | ||
1266 | if (!AddResource(stuff->shmseg, ShmSegType, (pointer) shmdesc)) { | |
1267 | close(fd); | |
1268 | return BadAlloc; | |
1269 | } | |
1270 | ||
1271 | if (WriteFdToClient(client, fd, TRUE) < 0) { | |
1272 | FreeResource(stuff->shmseg, RT_NONE); | |
1273 | close(fd); | |
1274 | return BadAlloc; | |
1275 | } | |
1276 | WriteToClient(client, sizeof (xShmCreateSegmentReply), &rep); | |
1277 | return Success; | |
1278 | } | |
1279 | #endif /* SHM_FD_PASSING */ | |
1280 | ||
1281 | static int | |
1282 | ProcShmDispatch(ClientPtr client) | |
1283 | { | |
1284 | REQUEST(xReq); | |
1285 | switch (stuff->data) { | |
1286 | case X_ShmQueryVersion: | |
1287 | return ProcShmQueryVersion(client); | |
1288 | case X_ShmAttach: | |
1289 | return ProcShmAttach(client); | |
1290 | case X_ShmDetach: | |
1291 | return ProcShmDetach(client); | |
1292 | case X_ShmPutImage: | |
1293 | #ifdef PANORAMIX | |
1294 | if (!noPanoramiXExtension) | |
1295 | return ProcPanoramiXShmPutImage(client); | |
1296 | #endif | |
1297 | return ProcShmPutImage(client); | |
1298 | case X_ShmGetImage: | |
1299 | #ifdef PANORAMIX | |
1300 | if (!noPanoramiXExtension) | |
1301 | return ProcPanoramiXShmGetImage(client); | |
1302 | #endif | |
1303 | return ProcShmGetImage(client); | |
1304 | case X_ShmCreatePixmap: | |
1305 | #ifdef PANORAMIX | |
1306 | if (!noPanoramiXExtension) | |
1307 | return ProcPanoramiXShmCreatePixmap(client); | |
1308 | #endif | |
1309 | return ProcShmCreatePixmap(client); | |
1310 | #ifdef SHM_FD_PASSING | |
1311 | case X_ShmAttachFd: | |
1312 | return ProcShmAttachFd(client); | |
1313 | case X_ShmCreateSegment: | |
1314 | return ProcShmCreateSegment(client); | |
1315 | #endif | |
1316 | default: | |
1317 | return BadRequest; | |
1318 | } | |
1319 | } | |
1320 | ||
1321 | static void | |
1322 | SShmCompletionEvent(xShmCompletionEvent * from, xShmCompletionEvent * to) | |
1323 | { | |
1324 | to->type = from->type; | |
1325 | cpswaps(from->sequenceNumber, to->sequenceNumber); | |
1326 | cpswapl(from->drawable, to->drawable); | |
1327 | cpswaps(from->minorEvent, to->minorEvent); | |
1328 | to->majorEvent = from->majorEvent; | |
1329 | cpswapl(from->shmseg, to->shmseg); | |
1330 | cpswapl(from->offset, to->offset); | |
1331 | } | |
1332 | ||
1333 | static int | |
1334 | SProcShmQueryVersion(ClientPtr client) | |
1335 | { | |
1336 | REQUEST(xShmQueryVersionReq); | |
1337 | ||
1338 | swaps(&stuff->length); | |
1339 | return ProcShmQueryVersion(client); | |
1340 | } | |
1341 | ||
1342 | static int | |
1343 | SProcShmAttach(ClientPtr client) | |
1344 | { | |
1345 | REQUEST(xShmAttachReq); | |
1346 | swaps(&stuff->length); | |
1347 | REQUEST_SIZE_MATCH(xShmAttachReq); | |
1348 | swapl(&stuff->shmseg); | |
1349 | swapl(&stuff->shmid); | |
1350 | return ProcShmAttach(client); | |
1351 | } | |
1352 | ||
1353 | static int | |
1354 | SProcShmDetach(ClientPtr client) | |
1355 | { | |
1356 | REQUEST(xShmDetachReq); | |
1357 | swaps(&stuff->length); | |
1358 | REQUEST_SIZE_MATCH(xShmDetachReq); | |
1359 | swapl(&stuff->shmseg); | |
1360 | return ProcShmDetach(client); | |
1361 | } | |
1362 | ||
1363 | static int | |
1364 | SProcShmPutImage(ClientPtr client) | |
1365 | { | |
1366 | REQUEST(xShmPutImageReq); | |
1367 | swaps(&stuff->length); | |
1368 | REQUEST_SIZE_MATCH(xShmPutImageReq); | |
1369 | swapl(&stuff->drawable); | |
1370 | swapl(&stuff->gc); | |
1371 | swaps(&stuff->totalWidth); | |
1372 | swaps(&stuff->totalHeight); | |
1373 | swaps(&stuff->srcX); | |
1374 | swaps(&stuff->srcY); | |
1375 | swaps(&stuff->srcWidth); | |
1376 | swaps(&stuff->srcHeight); | |
1377 | swaps(&stuff->dstX); | |
1378 | swaps(&stuff->dstY); | |
1379 | swapl(&stuff->shmseg); | |
1380 | swapl(&stuff->offset); | |
1381 | return ProcShmPutImage(client); | |
1382 | } | |
1383 | ||
1384 | static int | |
1385 | SProcShmGetImage(ClientPtr client) | |
1386 | { | |
1387 | REQUEST(xShmGetImageReq); | |
1388 | swaps(&stuff->length); | |
1389 | REQUEST_SIZE_MATCH(xShmGetImageReq); | |
1390 | swapl(&stuff->drawable); | |
1391 | swaps(&stuff->x); | |
1392 | swaps(&stuff->y); | |
1393 | swaps(&stuff->width); | |
1394 | swaps(&stuff->height); | |
1395 | swapl(&stuff->planeMask); | |
1396 | swapl(&stuff->shmseg); | |
1397 | swapl(&stuff->offset); | |
1398 | return ProcShmGetImage(client); | |
1399 | } | |
1400 | ||
1401 | static int | |
1402 | SProcShmCreatePixmap(ClientPtr client) | |
1403 | { | |
1404 | REQUEST(xShmCreatePixmapReq); | |
1405 | swaps(&stuff->length); | |
1406 | REQUEST_SIZE_MATCH(xShmCreatePixmapReq); | |
1407 | swapl(&stuff->pid); | |
1408 | swapl(&stuff->drawable); | |
1409 | swaps(&stuff->width); | |
1410 | swaps(&stuff->height); | |
1411 | swapl(&stuff->shmseg); | |
1412 | swapl(&stuff->offset); | |
1413 | return ProcShmCreatePixmap(client); | |
1414 | } | |
1415 | ||
1416 | #ifdef SHM_FD_PASSING | |
1417 | static int | |
1418 | SProcShmAttachFd(ClientPtr client) | |
1419 | { | |
1420 | REQUEST(xShmAttachFdReq); | |
1421 | SetReqFds(client, 1); | |
1422 | swaps(&stuff->length); | |
1423 | REQUEST_SIZE_MATCH(xShmAttachFdReq); | |
1424 | swapl(&stuff->shmseg); | |
1425 | return ProcShmAttachFd(client); | |
1426 | } | |
1427 | ||
1428 | static int | |
1429 | SProcShmCreateSegment(ClientPtr client) | |
1430 | { | |
1431 | REQUEST(xShmCreateSegmentReq); | |
1432 | swaps(&stuff->length); | |
1433 | REQUEST_SIZE_MATCH(xShmCreateSegmentReq); | |
1434 | swapl(&stuff->shmseg); | |
1435 | swapl(&stuff->size); | |
1436 | return ProcShmCreateSegment(client); | |
1437 | } | |
1438 | #endif /* SHM_FD_PASSING */ | |
1439 | ||
1440 | static int | |
1441 | SProcShmDispatch(ClientPtr client) | |
1442 | { | |
1443 | REQUEST(xReq); | |
1444 | switch (stuff->data) { | |
1445 | case X_ShmQueryVersion: | |
1446 | return SProcShmQueryVersion(client); | |
1447 | case X_ShmAttach: | |
1448 | return SProcShmAttach(client); | |
1449 | case X_ShmDetach: | |
1450 | return SProcShmDetach(client); | |
1451 | case X_ShmPutImage: | |
1452 | return SProcShmPutImage(client); | |
1453 | case X_ShmGetImage: | |
1454 | return SProcShmGetImage(client); | |
1455 | case X_ShmCreatePixmap: | |
1456 | return SProcShmCreatePixmap(client); | |
1457 | #ifdef SHM_FD_PASSING | |
1458 | case X_ShmAttachFd: | |
1459 | return SProcShmAttachFd(client); | |
1460 | case X_ShmCreateSegment: | |
1461 | return SProcShmCreateSegment(client); | |
1462 | #endif | |
1463 | default: | |
1464 | return BadRequest; | |
1465 | } | |
1466 | } | |
1467 | ||
1468 | void | |
1469 | ShmExtensionInit(void) | |
1470 | { | |
1471 | ExtensionEntry *extEntry; | |
1472 | int i; | |
1473 | ||
1474 | #ifdef MUST_CHECK_FOR_SHM_SYSCALL | |
1475 | if (!CheckForShmSyscall()) { | |
1476 | ErrorF("MIT-SHM extension disabled due to lack of kernel support\n"); | |
1477 | return; | |
1478 | } | |
1479 | #endif | |
1480 | ||
1481 | if (!ShmRegisterPrivates()) | |
1482 | return; | |
1483 | ||
1484 | sharedPixmaps = xFalse; | |
1485 | { | |
1486 | sharedPixmaps = xTrue; | |
1487 | for (i = 0; i < screenInfo.numScreens; i++) { | |
1488 | ShmScrPrivateRec *screen_priv = | |
1489 | ShmInitScreenPriv(screenInfo.screens[i]); | |
1490 | if (!screen_priv->shmFuncs) | |
1491 | screen_priv->shmFuncs = &miFuncs; | |
1492 | if (!screen_priv->shmFuncs->CreatePixmap) | |
1493 | sharedPixmaps = xFalse; | |
1494 | } | |
1495 | if (sharedPixmaps) | |
1496 | for (i = 0; i < screenInfo.numScreens; i++) { | |
1497 | ShmScrPrivateRec *screen_priv = | |
1498 | ShmGetScreenPriv(screenInfo.screens[i]); | |
1499 | screen_priv->destroyPixmap = | |
1500 | screenInfo.screens[i]->DestroyPixmap; | |
1501 | screenInfo.screens[i]->DestroyPixmap = ShmDestroyPixmap; | |
1502 | } | |
1503 | } | |
1504 | ShmSegType = CreateNewResourceType(ShmDetachSegment, "ShmSeg"); | |
1505 | if (ShmSegType && | |
1506 | (extEntry = AddExtension(SHMNAME, ShmNumberEvents, ShmNumberErrors, | |
1507 | ProcShmDispatch, SProcShmDispatch, | |
1508 | ShmResetProc, StandardMinorOpcode))) { | |
1509 | ShmReqCode = (unsigned char) extEntry->base; | |
1510 | ShmCompletionCode = extEntry->eventBase; | |
1511 | BadShmSegCode = extEntry->errorBase; | |
1512 | SetResourceTypeErrorValue(ShmSegType, BadShmSegCode); | |
1513 | EventSwapVector[ShmCompletionCode] = (EventSwapPtr) SShmCompletionEvent; | |
1514 | } | |
1515 | } |