1 /*
2    Copyright (c) 2002  XFree86 Inc
3 */
4 
5 #ifdef HAVE_DIX_CONFIG_H
6 #include <dix-config.h>
7 #endif
8 
9 #include <stdio.h>
10 #include <string.h>
11 #include <X11/X.h>
12 #include <X11/Xproto.h>
13 #include <assert.h>
14 #include "misc.h"
15 #include "os.h"
16 #include "dixstruct.h"
17 #include "extnsionst.h"
18 #include "swaprep.h"
19 #include "registry.h"
20 #include <X11/extensions/XResproto.h>
21 #include "pixmapstr.h"
22 #include "windowstr.h"
23 #include "gcstruct.h"
24 #include "extinit.h"
25 #include "protocol-versions.h"
26 #include "client.h"
27 #include "list.h"
28 #include "misc.h"
29 #include <string.h>
30 #include "hashtable.h"
31 #include "picturestr.h"
32 
33 #ifdef COMPOSITE
34 #include "compint.h"
35 #endif
36 
37 /** @brief Holds fragments of responses for ConstructClientIds.
38  *
39  *  note: there is no consideration for data alignment */
40 typedef struct {
41     struct xorg_list l;
42     int   bytes;
43     /* data follows */
44 } FragmentList;
45 
46 #define FRAGMENT_DATA(ptr) ((void*) ((char*) (ptr) + sizeof(FragmentList)))
47 
48 /** @brief Holds structure for the generated response to
49            ProcXResQueryClientIds; used by ConstructClientId* -functions */
50 typedef struct {
51     int           numIds;
52     int           resultBytes;
53     struct xorg_list   response;
54     int           sentClientMasks[MAXCLIENTS];
55 } ConstructClientIdCtx;
56 
57 /** @brief Holds the structure for information required to
58            generate the response to XResQueryResourceBytes. In addition
59            to response it contains information on the query as well,
60            as well as some volatile information required by a few
61            functions that cannot take that information directly
62            via a parameter, as they are called via already-existing
63            higher order functions. */
64 typedef struct {
65     ClientPtr     sendClient;
66     int           numSizes;
67     int           resultBytes;
68     struct xorg_list response;
69     int           status;
70     long          numSpecs;
71     xXResResourceIdSpec *specs;
72     HashTable     visitedResources;
73 
74     /* Used by AddSubResourceSizeSpec when AddResourceSizeValue is
75        handling cross-references */
76     HashTable     visitedSubResources;
77 
78     /* used when ConstructResourceBytesCtx is passed to
79        AddResourceSizeValue2 via FindClientResourcesByType */
80     RESTYPE       resType;
81 
82     /* used when ConstructResourceBytesCtx is passed to
83        AddResourceSizeValueByResource from ConstructResourceBytesByResource */
84     xXResResourceIdSpec       *curSpec;
85 
86     /** Used when iterating through a single resource's subresources
87 
88         @see AddSubResourceSizeSpec */
89     xXResResourceSizeValue    *sizeValue;
90 } ConstructResourceBytesCtx;
91 
92 /** @brief Allocate and add a sequence of bytes at the end of a fragment list.
93            Call DestroyFragments to release the list.
94 
95     @param frags A pointer to head of an initialized linked list
96     @param bytes Number of bytes to allocate
97     @return Returns a pointer to the allocated non-zeroed region
98             that is to be filled by the caller. On error (out of memory)
99             returns NULL and makes no changes to the list.
100 */
101 static void *
AddFragment(struct xorg_list * frags,int bytes)102 AddFragment(struct xorg_list *frags, int bytes)
103 {
104     FragmentList *f = malloc(sizeof(FragmentList) + bytes);
105     if (!f) {
106         return NULL;
107     } else {
108         f->bytes = bytes;
109         xorg_list_add(&f->l, frags->prev);
110         return (char*) f + sizeof(*f);
111     }
112 }
113 
114 /** @brief Sends all fragments in the list to the client. Does not
115            free anything.
116 
117     @param client The client to send the fragments to
118     @param frags The head of the list of fragments
119 */
120 static void
WriteFragmentsToClient(ClientPtr client,struct xorg_list * frags)121 WriteFragmentsToClient(ClientPtr client, struct xorg_list *frags)
122 {
123     FragmentList *it;
124     xorg_list_for_each_entry(it, frags, l) {
125         WriteToClient(client, it->bytes, (char*) it + sizeof(*it));
126     }
127 }
128 
129 /** @brief Frees a list of fragments. Does not free() root node.
130 
131     @param frags The head of the list of fragments
132 */
133 static void
DestroyFragments(struct xorg_list * frags)134 DestroyFragments(struct xorg_list *frags)
135 {
136     FragmentList *it, *tmp;
137     xorg_list_for_each_entry_safe(it, tmp, frags, l) {
138         xorg_list_del(&it->l);
139         free(it);
140     }
141 }
142 
143 /** @brief Constructs a context record for ConstructClientId* functions
144            to use */
145 static void
InitConstructClientIdCtx(ConstructClientIdCtx * ctx)146 InitConstructClientIdCtx(ConstructClientIdCtx *ctx)
147 {
148     ctx->numIds = 0;
149     ctx->resultBytes = 0;
150     xorg_list_init(&ctx->response);
151     memset(ctx->sentClientMasks, 0, sizeof(ctx->sentClientMasks));
152 }
153 
154 /** @brief Destroys a context record, releases all memory (except the storage
155            for *ctx itself) */
156 static void
DestroyConstructClientIdCtx(ConstructClientIdCtx * ctx)157 DestroyConstructClientIdCtx(ConstructClientIdCtx *ctx)
158 {
159     DestroyFragments(&ctx->response);
160 }
161 
162 static Bool
InitConstructResourceBytesCtx(ConstructResourceBytesCtx * ctx,ClientPtr sendClient,long numSpecs,xXResResourceIdSpec * specs)163 InitConstructResourceBytesCtx(ConstructResourceBytesCtx *ctx,
164                               ClientPtr                  sendClient,
165                               long                       numSpecs,
166                               xXResResourceIdSpec       *specs)
167 {
168     ctx->sendClient = sendClient;
169     ctx->numSizes = 0;
170     ctx->resultBytes = 0;
171     xorg_list_init(&ctx->response);
172     ctx->status = Success;
173     ctx->numSpecs = numSpecs;
174     ctx->specs = specs;
175     ctx->visitedResources = ht_create(sizeof(XID), 0,
176                                       ht_resourceid_hash, ht_resourceid_compare,
177                                       NULL);
178 
179     if (!ctx->visitedResources) {
180         return FALSE;
181     } else {
182         return TRUE;
183     }
184 }
185 
186 static void
DestroyConstructResourceBytesCtx(ConstructResourceBytesCtx * ctx)187 DestroyConstructResourceBytesCtx(ConstructResourceBytesCtx *ctx)
188 {
189     DestroyFragments(&ctx->response);
190     ht_destroy(ctx->visitedResources);
191 }
192 
193 static int
ProcXResQueryVersion(ClientPtr client)194 ProcXResQueryVersion(ClientPtr client)
195 {
196     xXResQueryVersionReply rep = {
197         .type = X_Reply,
198         .sequenceNumber = client->sequence,
199         .length = 0,
200         .server_major = SERVER_XRES_MAJOR_VERSION,
201         .server_minor = SERVER_XRES_MINOR_VERSION
202     };
203 
204     REQUEST_SIZE_MATCH(xXResQueryVersionReq);
205 
206     if (client->swapped) {
207         swaps(&rep.sequenceNumber);
208         swapl(&rep.length);
209         swaps(&rep.server_major);
210         swaps(&rep.server_minor);
211     }
212     WriteToClient(client, sizeof(xXResQueryVersionReply), &rep);
213     return Success;
214 }
215 
216 static int
ProcXResQueryClients(ClientPtr client)217 ProcXResQueryClients(ClientPtr client)
218 {
219     /* REQUEST(xXResQueryClientsReq); */
220     xXResQueryClientsReply rep;
221     int *current_clients;
222     int i, num_clients;
223 
224     REQUEST_SIZE_MATCH(xXResQueryClientsReq);
225 
226     current_clients = xallocarray(currentMaxClients, sizeof(int));
227 
228     num_clients = 0;
229     for (i = 0; i < currentMaxClients; i++) {
230         if (clients[i]) {
231             current_clients[num_clients] = i;
232             num_clients++;
233         }
234     }
235 
236     rep = (xXResQueryClientsReply) {
237         .type = X_Reply,
238         .sequenceNumber = client->sequence,
239         .length = bytes_to_int32(num_clients * sz_xXResClient),
240         .num_clients = num_clients
241     };
242     if (client->swapped) {
243         swaps(&rep.sequenceNumber);
244         swapl(&rep.length);
245         swapl(&rep.num_clients);
246     }
247     WriteToClient(client, sizeof(xXResQueryClientsReply), &rep);
248 
249     if (num_clients) {
250         xXResClient scratch;
251 
252         for (i = 0; i < num_clients; i++) {
253             scratch.resource_base = clients[current_clients[i]]->clientAsMask;
254             scratch.resource_mask = RESOURCE_ID_MASK;
255 
256             if (client->swapped) {
257                 swapl(&scratch.resource_base);
258                 swapl(&scratch.resource_mask);
259             }
260             WriteToClient(client, sz_xXResClient, &scratch);
261         }
262     }
263 
264     free(current_clients);
265 
266     return Success;
267 }
268 
269 static void
ResFindAllRes(void * value,XID id,RESTYPE type,void * cdata)270 ResFindAllRes(void *value, XID id, RESTYPE type, void *cdata)
271 {
272     int *counts = (int *) cdata;
273 
274     counts[(type & TypeMask) - 1]++;
275 }
276 
277 static CARD32
resourceTypeAtom(int i)278 resourceTypeAtom(int i)
279 {
280     CARD32 ret;
281 
282     const char *name = LookupResourceName(i);
283     if (strcmp(name, XREGISTRY_UNKNOWN))
284         ret = MakeAtom(name, strlen(name), TRUE);
285     else {
286         char buf[40];
287 
288         snprintf(buf, sizeof(buf), "Unregistered resource %i", i + 1);
289         ret = MakeAtom(buf, strlen(buf), TRUE);
290     }
291 
292     return ret;
293 }
294 
295 static int
ProcXResQueryClientResources(ClientPtr client)296 ProcXResQueryClientResources(ClientPtr client)
297 {
298     REQUEST(xXResQueryClientResourcesReq);
299     xXResQueryClientResourcesReply rep;
300     int i, clientID, num_types;
301     int *counts;
302 
303     REQUEST_SIZE_MATCH(xXResQueryClientResourcesReq);
304 
305     clientID = CLIENT_ID(stuff->xid);
306 
307     if ((clientID >= currentMaxClients) || !clients[clientID]) {
308         client->errorValue = stuff->xid;
309         return BadValue;
310     }
311 
312     counts = calloc(lastResourceType + 1, sizeof(int));
313 
314     FindAllClientResources(clients[clientID], ResFindAllRes, counts);
315 
316     num_types = 0;
317 
318     for (i = 0; i <= lastResourceType; i++) {
319         if (counts[i])
320             num_types++;
321     }
322 
323     rep = (xXResQueryClientResourcesReply) {
324         .type = X_Reply,
325         .sequenceNumber = client->sequence,
326         .length = bytes_to_int32(num_types * sz_xXResType),
327         .num_types = num_types
328     };
329     if (client->swapped) {
330         swaps(&rep.sequenceNumber);
331         swapl(&rep.length);
332         swapl(&rep.num_types);
333     }
334 
335     WriteToClient(client, sizeof(xXResQueryClientResourcesReply), &rep);
336 
337     if (num_types) {
338         xXResType scratch;
339 
340         for (i = 0; i < lastResourceType; i++) {
341             if (!counts[i])
342                 continue;
343 
344             scratch.resource_type = resourceTypeAtom(i + 1);
345             scratch.count = counts[i];
346 
347             if (client->swapped) {
348                 swapl(&scratch.resource_type);
349                 swapl(&scratch.count);
350             }
351             WriteToClient(client, sz_xXResType, &scratch);
352         }
353     }
354 
355     free(counts);
356 
357     return Success;
358 }
359 
360 static void
ResFindResourcePixmaps(void * value,XID id,RESTYPE type,void * cdata)361 ResFindResourcePixmaps(void *value, XID id, RESTYPE type, void *cdata)
362 {
363     SizeType sizeFunc = GetResourceTypeSizeFunc(type);
364     ResourceSizeRec size = { 0, 0, 0 };
365     unsigned long *bytes = cdata;
366 
367     sizeFunc(value, id, &size);
368     *bytes += size.pixmapRefSize;
369 }
370 
371 static int
ProcXResQueryClientPixmapBytes(ClientPtr client)372 ProcXResQueryClientPixmapBytes(ClientPtr client)
373 {
374     REQUEST(xXResQueryClientPixmapBytesReq);
375     xXResQueryClientPixmapBytesReply rep;
376     int clientID;
377     unsigned long bytes;
378 
379     REQUEST_SIZE_MATCH(xXResQueryClientPixmapBytesReq);
380 
381     clientID = CLIENT_ID(stuff->xid);
382 
383     if ((clientID >= currentMaxClients) || !clients[clientID]) {
384         client->errorValue = stuff->xid;
385         return BadValue;
386     }
387 
388     bytes = 0;
389 
390     FindAllClientResources(clients[clientID], ResFindResourcePixmaps,
391                            (void *) (&bytes));
392 
393     rep = (xXResQueryClientPixmapBytesReply) {
394         .type = X_Reply,
395         .sequenceNumber = client->sequence,
396         .length = 0,
397         .bytes = bytes,
398 #ifdef _XSERVER64
399         .bytes_overflow = bytes >> 32
400 #else
401         .bytes_overflow = 0
402 #endif
403     };
404     if (client->swapped) {
405         swaps(&rep.sequenceNumber);
406         swapl(&rep.length);
407         swapl(&rep.bytes);
408         swapl(&rep.bytes_overflow);
409     }
410     WriteToClient(client, sizeof(xXResQueryClientPixmapBytesReply), &rep);
411 
412     return Success;
413 }
414 
415 /** @brief Finds out if a client's information need to be put into the
416     response; marks client having been handled, if that is the case.
417 
418     @param client   The client to send information about
419     @param mask     The request mask (0 to send everything, otherwise a
420                     bitmask of X_XRes*Mask)
421     @param ctx      The context record that tells which clients and id types
422                     have been already handled
423     @param sendMask Which id type are we now considering. One of X_XRes*Mask.
424 
425     @return Returns TRUE if the client information needs to be on the
426             response, otherwise FALSE.
427 */
428 static Bool
WillConstructMask(ClientPtr client,CARD32 mask,ConstructClientIdCtx * ctx,int sendMask)429 WillConstructMask(ClientPtr client, CARD32 mask,
430                   ConstructClientIdCtx *ctx, int sendMask)
431 {
432     if ((!mask || (mask & sendMask))
433         && !(ctx->sentClientMasks[client->index] & sendMask)) {
434         ctx->sentClientMasks[client->index] |= sendMask;
435         return TRUE;
436     } else {
437         return FALSE;
438     }
439 }
440 
441 /** @brief Constructs a response about a single client, based on a certain
442            client id spec
443 
444     @param sendClient Which client wishes to receive this answer. Used for
445                       byte endianness.
446     @param client     Which client are we considering.
447     @param mask       The client id spec mask indicating which information
448                       we want about this client.
449     @param ctx        The context record containing the constructed response
450                       and information on which clients and masks have been
451                       already handled.
452 
453     @return Return TRUE if everything went OK, otherwise FALSE which indicates
454             a memory allocation problem.
455 */
456 static Bool
ConstructClientIdValue(ClientPtr sendClient,ClientPtr client,CARD32 mask,ConstructClientIdCtx * ctx)457 ConstructClientIdValue(ClientPtr sendClient, ClientPtr client, CARD32 mask,
458                        ConstructClientIdCtx *ctx)
459 {
460     xXResClientIdValue rep;
461 
462     rep.spec.client = client->clientAsMask;
463     if (client->swapped) {
464         swapl (&rep.spec.client);
465     }
466 
467     if (WillConstructMask(client, mask, ctx, X_XResClientXIDMask)) {
468         void *ptr = AddFragment(&ctx->response, sizeof(rep));
469         if (!ptr) {
470             return FALSE;
471         }
472 
473         rep.spec.mask = X_XResClientXIDMask;
474         rep.length = 0;
475         if (sendClient->swapped) {
476             swapl (&rep.spec.mask);
477             /* swapl (&rep.length, n); - not required for rep.length = 0 */
478         }
479 
480         memcpy(ptr, &rep, sizeof(rep));
481 
482         ctx->resultBytes += sizeof(rep);
483         ++ctx->numIds;
484     }
485     if (WillConstructMask(client, mask, ctx, X_XResLocalClientPIDMask)) {
486         pid_t pid = GetClientPid(client);
487 
488         if (pid != -1) {
489             void *ptr = AddFragment(&ctx->response,
490                                     sizeof(rep) + sizeof(CARD32));
491             CARD32 *value = (void*) ((char*) ptr + sizeof(rep));
492 
493             if (!ptr) {
494                 return FALSE;
495             }
496 
497             rep.spec.mask = X_XResLocalClientPIDMask;
498             rep.length = 4;
499 
500             if (sendClient->swapped) {
501                 swapl (&rep.spec.mask);
502                 swapl (&rep.length);
503             }
504 
505             if (sendClient->swapped) {
506                 swapl (value);
507             }
508             memcpy(ptr, &rep, sizeof(rep));
509             *value = pid;
510 
511             ctx->resultBytes += sizeof(rep) + sizeof(CARD32);
512             ++ctx->numIds;
513         }
514     }
515 
516     /* memory allocation errors earlier may return with FALSE */
517     return TRUE;
518 }
519 
520 /** @brief Constructs a response about all clients, based on a client id specs
521 
522     @param client   Which client which we are constructing the response for.
523     @param numSpecs Number of client id specs in specs
524     @param specs    Client id specs
525 
526     @return Return Success if everything went OK, otherwise a Bad* (currently
527             BadAlloc or BadValue)
528 */
529 static int
ConstructClientIds(ClientPtr client,int numSpecs,xXResClientIdSpec * specs,ConstructClientIdCtx * ctx)530 ConstructClientIds(ClientPtr client,
531                    int numSpecs, xXResClientIdSpec* specs,
532                    ConstructClientIdCtx *ctx)
533 {
534     int specIdx;
535 
536     for (specIdx = 0; specIdx < numSpecs; ++specIdx) {
537         if (specs[specIdx].client == 0) {
538             int c;
539             for (c = 0; c < currentMaxClients; ++c) {
540                 if (clients[c]) {
541                     if (!ConstructClientIdValue(client, clients[c],
542                                                 specs[specIdx].mask, ctx)) {
543                         return BadAlloc;
544                     }
545                 }
546             }
547         } else {
548             int clientID = CLIENT_ID(specs[specIdx].client);
549 
550             if ((clientID < currentMaxClients) && clients[clientID]) {
551                 if (!ConstructClientIdValue(client, clients[clientID],
552                                             specs[specIdx].mask, ctx)) {
553                     return BadAlloc;
554                 }
555             }
556         }
557     }
558 
559     /* memory allocation errors earlier may return with BadAlloc */
560     return Success;
561 }
562 
563 /** @brief Response to XResQueryClientIds request introduced in XResProto v1.2
564 
565     @param client Which client which we are constructing the response for.
566 
567     @return Returns the value returned from ConstructClientIds with the same
568             semantics
569 */
570 static int
ProcXResQueryClientIds(ClientPtr client)571 ProcXResQueryClientIds (ClientPtr client)
572 {
573     REQUEST(xXResQueryClientIdsReq);
574 
575     xXResClientIdSpec        *specs = (void*) ((char*) stuff + sizeof(*stuff));
576     int                       rc;
577     ConstructClientIdCtx      ctx;
578 
579     InitConstructClientIdCtx(&ctx);
580 
581     REQUEST_AT_LEAST_SIZE(xXResQueryClientIdsReq);
582     REQUEST_FIXED_SIZE(xXResQueryClientIdsReq,
583                        stuff->numSpecs * sizeof(specs[0]));
584 
585     rc = ConstructClientIds(client, stuff->numSpecs, specs, &ctx);
586 
587     if (rc == Success) {
588         xXResQueryClientIdsReply  rep = {
589             .type = X_Reply,
590             .sequenceNumber = client->sequence,
591             .length = bytes_to_int32(ctx.resultBytes),
592             .numIds = ctx.numIds
593         };
594 
595         assert((ctx.resultBytes & 3) == 0);
596 
597         if (client->swapped) {
598             swaps (&rep.sequenceNumber);
599             swapl (&rep.length);
600             swapl (&rep.numIds);
601         }
602 
603         WriteToClient(client, sizeof(rep), &rep);
604         WriteFragmentsToClient(client, &ctx.response);
605     }
606 
607     DestroyConstructClientIdCtx(&ctx);
608 
609     return rc;
610 }
611 
612 /** @brief Swaps xXResResourceIdSpec endianness */
613 static void
SwapXResResourceIdSpec(xXResResourceIdSpec * spec)614 SwapXResResourceIdSpec(xXResResourceIdSpec *spec)
615 {
616     swapl(&spec->resource);
617     swapl(&spec->type);
618 }
619 
620 /** @brief Swaps xXResResourceSizeSpec endianness */
621 static void
SwapXResResourceSizeSpec(xXResResourceSizeSpec * size)622 SwapXResResourceSizeSpec(xXResResourceSizeSpec *size)
623 {
624     SwapXResResourceIdSpec(&size->spec);
625     swapl(&size->bytes);
626     swapl(&size->refCount);
627     swapl(&size->useCount);
628 }
629 
630 /** @brief Swaps xXResResourceSizeValue endianness */
631 static void
SwapXResResourceSizeValue(xXResResourceSizeValue * rep)632 SwapXResResourceSizeValue(xXResResourceSizeValue *rep)
633 {
634     SwapXResResourceSizeSpec(&rep->size);
635     swapl(&rep->numCrossReferences);
636 }
637 
638 /** @brief Swaps the response bytes */
639 static void
SwapXResQueryResourceBytes(struct xorg_list * response)640 SwapXResQueryResourceBytes(struct xorg_list *response)
641 {
642     struct xorg_list *it = response->next;
643     int c;
644 
645     while (it != response) {
646         xXResResourceSizeValue *value = FRAGMENT_DATA(it);
647         it = it->next;
648         for (c = 0; c < value->numCrossReferences; ++c) {
649             xXResResourceSizeSpec *spec = FRAGMENT_DATA(it);
650             SwapXResResourceSizeSpec(spec);
651             it = it->next;
652         }
653         SwapXResResourceSizeValue(value);
654     }
655 }
656 
657 /** @brief Adds xXResResourceSizeSpec describing a resource's size into
658            the buffer contained in the context. The resource is considered
659            to be a subresource.
660 
661    @see AddResourceSizeValue
662 
663    @param[in] value     The X resource object on which to add information
664                         about to the buffer
665    @param[in] id        The ID of the X resource
666    @param[in] type      The type of the X resource
667    @param[in/out] cdata The context object of type ConstructResourceBytesCtx.
668                         Void pointer type is used here to satisfy the type
669                         FindRes
670 */
671 static void
AddSubResourceSizeSpec(void * value,XID id,RESTYPE type,void * cdata)672 AddSubResourceSizeSpec(void *value,
673                        XID id,
674                        RESTYPE type,
675                        void *cdata)
676 {
677     ConstructResourceBytesCtx *ctx = cdata;
678 
679     if (ctx->status == Success) {
680         xXResResourceSizeSpec **prevCrossRef =
681           ht_find(ctx->visitedSubResources, &value);
682         if (!prevCrossRef) {
683             Bool ok = TRUE;
684             xXResResourceSizeSpec *crossRef =
685                 AddFragment(&ctx->response, sizeof(xXResResourceSizeSpec));
686             ok = ok && crossRef != NULL;
687             if (ok) {
688                 xXResResourceSizeSpec **p;
689                 p = ht_add(ctx->visitedSubResources, &value);
690                 if (!p) {
691                     ok = FALSE;
692                 } else {
693                     *p = crossRef;
694                 }
695             }
696             if (!ok) {
697                 ctx->status = BadAlloc;
698             } else {
699                 SizeType sizeFunc = GetResourceTypeSizeFunc(type);
700                 ResourceSizeRec size = { 0, 0, 0 };
701                 sizeFunc(value, id, &size);
702 
703                 crossRef->spec.resource = id;
704                 crossRef->spec.type = resourceTypeAtom(type);
705                 crossRef->bytes = size.resourceSize;
706                 crossRef->refCount = size.refCnt;
707                 crossRef->useCount = 1;
708 
709                 ++ctx->sizeValue->numCrossReferences;
710 
711                 ctx->resultBytes += sizeof(*crossRef);
712             }
713         } else {
714             /* if we have visited the subresource earlier (from current parent
715                resource), just increase its use count by one */
716             ++(*prevCrossRef)->useCount;
717         }
718     }
719 }
720 
721 /** @brief Adds xXResResourceSizeValue describing a resource's size into
722            the buffer contained in the context. In addition, the
723            subresources are iterated and added as xXResResourceSizeSpec's
724            by using AddSubResourceSizeSpec
725 
726    @see AddSubResourceSizeSpec
727 
728    @param[in] value     The X resource object on which to add information
729                         about to the buffer
730    @param[in] id        The ID of the X resource
731    @param[in] type      The type of the X resource
732    @param[in/out] cdata The context object of type ConstructResourceBytesCtx.
733                         Void pointer type is used here to satisfy the type
734                         FindRes
735 */
736 static void
AddResourceSizeValue(void * ptr,XID id,RESTYPE type,void * cdata)737 AddResourceSizeValue(void *ptr, XID id, RESTYPE type, void *cdata)
738 {
739     ConstructResourceBytesCtx *ctx = cdata;
740     if (ctx->status == Success &&
741         !ht_find(ctx->visitedResources, &id)) {
742         Bool ok = TRUE;
743         HashTable ht;
744         HtGenericHashSetupRec htSetup = {
745             .keySize = sizeof(void*)
746         };
747 
748         /* it doesn't matter that we don't undo the work done here
749          * immediately. All but ht_init will be undone at the end
750          * of the request and there can happen no failure after
751          * ht_init, so we don't need to clean it up here in any
752          * special way */
753 
754         xXResResourceSizeValue *value =
755             AddFragment(&ctx->response, sizeof(xXResResourceSizeValue));
756         if (!value) {
757             ok = FALSE;
758         }
759         ok = ok && ht_add(ctx->visitedResources, &id);
760         if (ok) {
761             ht = ht_create(htSetup.keySize,
762                            sizeof(xXResResourceSizeSpec*),
763                            ht_generic_hash, ht_generic_compare,
764                            &htSetup);
765             ok = ok && ht;
766         }
767 
768         if (!ok) {
769             ctx->status = BadAlloc;
770         } else {
771             SizeType sizeFunc = GetResourceTypeSizeFunc(type);
772             ResourceSizeRec size = { 0, 0, 0 };
773 
774             sizeFunc(ptr, id, &size);
775 
776             value->size.spec.resource = id;
777             value->size.spec.type = resourceTypeAtom(type);
778             value->size.bytes = size.resourceSize;
779             value->size.refCount = size.refCnt;
780             value->size.useCount = 1;
781             value->numCrossReferences = 0;
782 
783             ctx->sizeValue = value;
784             ctx->visitedSubResources = ht;
785             FindSubResources(ptr, type, AddSubResourceSizeSpec, ctx);
786             ctx->visitedSubResources = NULL;
787             ctx->sizeValue = NULL;
788 
789             ctx->resultBytes += sizeof(*value);
790             ++ctx->numSizes;
791 
792             ht_destroy(ht);
793         }
794     }
795 }
796 
797 /** @brief A variant of AddResourceSizeValue that passes the resource type
798            through the context object to satisfy the type FindResType
799 
800    @see AddResourceSizeValue
801 
802    @param[in] ptr        The resource
803    @param[in] id         The resource ID
804    @param[in/out] cdata  The context object that contains the resource type
805 */
806 static void
AddResourceSizeValueWithResType(void * ptr,XID id,void * cdata)807 AddResourceSizeValueWithResType(void *ptr, XID id, void *cdata)
808 {
809     ConstructResourceBytesCtx *ctx = cdata;
810     AddResourceSizeValue(ptr, id, ctx->resType, cdata);
811 }
812 
813 /** @brief Adds the information of a resource into the buffer if it matches
814            the match condition.
815 
816    @see AddResourceSizeValue
817 
818    @param[in] ptr        The resource
819    @param[in] id         The resource ID
820    @param[in] type       The resource type
821    @param[in/out] cdata  The context object as a void pointer to satisfy the
822                          type FindAllRes
823 */
824 static void
AddResourceSizeValueByResource(void * ptr,XID id,RESTYPE type,void * cdata)825 AddResourceSizeValueByResource(void *ptr, XID id, RESTYPE type, void *cdata)
826 {
827     ConstructResourceBytesCtx *ctx = cdata;
828     xXResResourceIdSpec *spec = ctx->curSpec;
829 
830     if ((!spec->type || spec->type == type) &&
831         (!spec->resource || spec->resource == id)) {
832         AddResourceSizeValue(ptr, id, type, ctx);
833     }
834 }
835 
836 /** @brief Add all resources of the client into the result buffer
837            disregarding all those specifications that specify the
838            resource by its ID. Those are handled by
839            ConstructResourceBytesByResource
840 
841    @see ConstructResourceBytesByResource
842 
843    @param[in] aboutClient  Which client is being considered
844    @param[in/out] ctx      The context that contains the resource id
845                            specifications as well as the result buffer
846 */
847 static void
ConstructClientResourceBytes(ClientPtr aboutClient,ConstructResourceBytesCtx * ctx)848 ConstructClientResourceBytes(ClientPtr aboutClient,
849                              ConstructResourceBytesCtx *ctx)
850 {
851     int specIdx;
852     for (specIdx = 0; specIdx < ctx->numSpecs; ++specIdx) {
853         xXResResourceIdSpec* spec = ctx->specs + specIdx;
854         if (spec->resource) {
855             /* these specs are handled elsewhere */
856         } else if (spec->type) {
857             ctx->resType = spec->type;
858             FindClientResourcesByType(aboutClient, spec->type,
859                                       AddResourceSizeValueWithResType, ctx);
860         } else {
861             FindAllClientResources(aboutClient, AddResourceSizeValue, ctx);
862         }
863     }
864 }
865 
866 /** @brief Add the sizes of all such resources that can are specified by
867            their ID in the resource id specification. The scan can
868            by limited to a client with the aboutClient parameter
869 
870    @see ConstructResourceBytesByResource
871 
872    @param[in] aboutClient  Which client is being considered. This may be None
873                            to mean all clients.
874    @param[in/out] ctx      The context that contains the resource id
875                            specifications as well as the result buffer. In
876                            addition this function uses the curSpec field to
877                            keep a pointer to the current resource id
878                            specification in it, which can be used by
879                            AddResourceSizeValueByResource .
880 */
881 static void
ConstructResourceBytesByResource(XID aboutClient,ConstructResourceBytesCtx * ctx)882 ConstructResourceBytesByResource(XID aboutClient, ConstructResourceBytesCtx *ctx)
883 {
884     int specIdx;
885     for (specIdx = 0; specIdx < ctx->numSpecs; ++specIdx) {
886         xXResResourceIdSpec *spec = ctx->specs + specIdx;
887         if (spec->resource) {
888             int cid = CLIENT_ID(spec->resource);
889             if (cid < currentMaxClients &&
890                 (aboutClient == None || cid == aboutClient)) {
891                 ClientPtr client = clients[cid];
892                 if (client) {
893                     ctx->curSpec = spec;
894                     FindAllClientResources(client,
895                                            AddResourceSizeValueByResource,
896                                            ctx);
897                 }
898             }
899         }
900     }
901 }
902 
903 /** @brief Build the resource size response for the given client
904            (or all if not specified) per the parameters set up
905            in the context object.
906 
907   @param[in] aboutClient  Which client to consider or None for all clients
908   @param[in/out] ctx      The context object that contains the request as well
909                           as the response buffer.
910 */
911 static int
ConstructResourceBytes(XID aboutClient,ConstructResourceBytesCtx * ctx)912 ConstructResourceBytes(XID aboutClient,
913                        ConstructResourceBytesCtx *ctx)
914 {
915     if (aboutClient) {
916         int clientIdx = CLIENT_ID(aboutClient);
917         ClientPtr client = NullClient;
918 
919         if ((clientIdx >= currentMaxClients) || !clients[clientIdx]) {
920             ctx->sendClient->errorValue = aboutClient;
921             return BadValue;
922         }
923 
924         client = clients[clientIdx];
925 
926         ConstructClientResourceBytes(client, ctx);
927         ConstructResourceBytesByResource(aboutClient, ctx);
928     } else {
929         int clientIdx;
930 
931         ConstructClientResourceBytes(NULL, ctx);
932 
933         for (clientIdx = 0; clientIdx < currentMaxClients; ++clientIdx) {
934             ClientPtr client = clients[clientIdx];
935 
936             if (client) {
937                 ConstructClientResourceBytes(client, ctx);
938             }
939         }
940 
941         ConstructResourceBytesByResource(None, ctx);
942     }
943 
944 
945     return ctx->status;
946 }
947 
948 /** @brief Implements the XResQueryResourceBytes of XResProto v1.2 */
949 static int
ProcXResQueryResourceBytes(ClientPtr client)950 ProcXResQueryResourceBytes (ClientPtr client)
951 {
952     REQUEST(xXResQueryResourceBytesReq);
953 
954     int                          rc;
955     ConstructResourceBytesCtx    ctx;
956 
957     REQUEST_AT_LEAST_SIZE(xXResQueryResourceBytesReq);
958     if (stuff->numSpecs > UINT32_MAX / sizeof(ctx.specs[0]))
959         return BadLength;
960     REQUEST_FIXED_SIZE(xXResQueryResourceBytesReq,
961                        stuff->numSpecs * sizeof(ctx.specs[0]));
962 
963     if (!InitConstructResourceBytesCtx(&ctx, client,
964                                        stuff->numSpecs,
965                                        (void*) ((char*) stuff +
966                                                 sz_xXResQueryResourceBytesReq))) {
967         return BadAlloc;
968     }
969 
970     rc = ConstructResourceBytes(stuff->client, &ctx);
971 
972     if (rc == Success) {
973         xXResQueryResourceBytesReply rep = {
974             .type = X_Reply,
975             .sequenceNumber = client->sequence,
976             .length = bytes_to_int32(ctx.resultBytes),
977             .numSizes = ctx.numSizes
978         };
979 
980         if (client->swapped) {
981             swaps (&rep.sequenceNumber);
982             swapl (&rep.length);
983             swapl (&rep.numSizes);
984 
985             SwapXResQueryResourceBytes(&ctx.response);
986         }
987 
988         WriteToClient(client, sizeof(rep), &rep);
989         WriteFragmentsToClient(client, &ctx.response);
990     }
991 
992     DestroyConstructResourceBytesCtx(&ctx);
993 
994     return rc;
995 }
996 
997 static int
ProcResDispatch(ClientPtr client)998 ProcResDispatch(ClientPtr client)
999 {
1000     REQUEST(xReq);
1001     switch (stuff->data) {
1002     case X_XResQueryVersion:
1003         return ProcXResQueryVersion(client);
1004     case X_XResQueryClients:
1005         return ProcXResQueryClients(client);
1006     case X_XResQueryClientResources:
1007         return ProcXResQueryClientResources(client);
1008     case X_XResQueryClientPixmapBytes:
1009         return ProcXResQueryClientPixmapBytes(client);
1010     case X_XResQueryClientIds:
1011         return ProcXResQueryClientIds(client);
1012     case X_XResQueryResourceBytes:
1013         return ProcXResQueryResourceBytes(client);
1014     default: break;
1015     }
1016 
1017     return BadRequest;
1018 }
1019 
1020 static int _X_COLD
SProcXResQueryVersion(ClientPtr client)1021 SProcXResQueryVersion(ClientPtr client)
1022 {
1023     REQUEST_SIZE_MATCH(xXResQueryVersionReq);
1024     return ProcXResQueryVersion(client);
1025 }
1026 
1027 static int _X_COLD
SProcXResQueryClientResources(ClientPtr client)1028 SProcXResQueryClientResources(ClientPtr client)
1029 {
1030     REQUEST(xXResQueryClientResourcesReq);
1031     REQUEST_SIZE_MATCH(xXResQueryClientResourcesReq);
1032     swapl(&stuff->xid);
1033     return ProcXResQueryClientResources(client);
1034 }
1035 
1036 static int _X_COLD
SProcXResQueryClientPixmapBytes(ClientPtr client)1037 SProcXResQueryClientPixmapBytes(ClientPtr client)
1038 {
1039     REQUEST(xXResQueryClientPixmapBytesReq);
1040     REQUEST_SIZE_MATCH(xXResQueryClientPixmapBytesReq);
1041     swapl(&stuff->xid);
1042     return ProcXResQueryClientPixmapBytes(client);
1043 }
1044 
1045 static int _X_COLD
SProcXResQueryClientIds(ClientPtr client)1046 SProcXResQueryClientIds (ClientPtr client)
1047 {
1048     REQUEST(xXResQueryClientIdsReq);
1049 
1050     REQUEST_AT_LEAST_SIZE (xXResQueryClientIdsReq);
1051     swapl(&stuff->numSpecs);
1052     return ProcXResQueryClientIds(client);
1053 }
1054 
1055 /** @brief Implements the XResQueryResourceBytes of XResProto v1.2.
1056     This variant byteswaps request contents before issuing the
1057     rest of the work to ProcXResQueryResourceBytes */
1058 static int _X_COLD
SProcXResQueryResourceBytes(ClientPtr client)1059 SProcXResQueryResourceBytes (ClientPtr client)
1060 {
1061     REQUEST(xXResQueryResourceBytesReq);
1062     int c;
1063     xXResResourceIdSpec *specs = (void*) ((char*) stuff + sizeof(*stuff));
1064 
1065     REQUEST_AT_LEAST_SIZE(xXResQueryResourceBytesReq);
1066     swapl(&stuff->numSpecs);
1067     REQUEST_FIXED_SIZE(xXResQueryResourceBytesReq,
1068                        stuff->numSpecs * sizeof(specs[0]));
1069 
1070     for (c = 0; c < stuff->numSpecs; ++c) {
1071         SwapXResResourceIdSpec(specs + c);
1072     }
1073 
1074     return ProcXResQueryResourceBytes(client);
1075 }
1076 
1077 static int _X_COLD
SProcResDispatch(ClientPtr client)1078 SProcResDispatch (ClientPtr client)
1079 {
1080     REQUEST(xReq);
1081     swaps(&stuff->length);
1082 
1083     switch (stuff->data) {
1084     case X_XResQueryVersion:
1085         return SProcXResQueryVersion(client);
1086     case X_XResQueryClients:   /* nothing to swap */
1087         return ProcXResQueryClients(client);
1088     case X_XResQueryClientResources:
1089         return SProcXResQueryClientResources(client);
1090     case X_XResQueryClientPixmapBytes:
1091         return SProcXResQueryClientPixmapBytes(client);
1092     case X_XResQueryClientIds:
1093         return SProcXResQueryClientIds(client);
1094     case X_XResQueryResourceBytes:
1095         return SProcXResQueryResourceBytes(client);
1096     default: break;
1097     }
1098 
1099     return BadRequest;
1100 }
1101 
1102 void
ResExtensionInit(void)1103 ResExtensionInit(void)
1104 {
1105     (void) AddExtension(XRES_NAME, 0, 0,
1106                         ProcResDispatch, SProcResDispatch,
1107                         NULL, StandardMinorOpcode);
1108 }
1109