Initial CAPS IPC structural encoding and decoding support. Note that the
[dragonfly.git] / lib / libcaps / caps_struct.c
1 /*
2  * Copyright (c) 2003 Matthew Dillon <dillon@backplane.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $DragonFly: src/lib/libcaps/caps_struct.c,v 1.1 2004/03/07 23:36:44 dillon Exp $
27  */
28
29 #include "defs.h"
30
31 static
32 int32_t
33 parsehex32(const u_int8_t *ptr, int len)
34 {
35     int neg = 0;
36     int32_t v = 0;
37     u_int8_t c;
38
39     if (len && ptr[0] == '-') {
40         neg = 1;
41         --len;
42         ++ptr;
43     }
44     while (len) {
45         v = v << 4;
46         c = *ptr;
47         if (c >= '0' && c <= '9')
48             v |= c - '0';
49         if (c >= 'a' && c <= 'f')
50             v |= c - ('a' - 10);
51         --len;
52         ++ptr;
53     }
54     if (neg)
55         v = -v;
56     return(v);
57 }
58
59 static
60 int64_t
61 parsehex64(const u_int8_t *ptr, int len)
62 {
63     int neg = 0;
64     int64_t v;
65
66     if (len && ptr[0] == '-') {
67         neg = 1;
68         --len;
69         ++ptr;
70     }
71
72     if (len > 4) {
73         v = parsehex32(ptr + len - 4, 4) |
74                 ((int64_t)parsehex32(ptr, len - 4) << 32);
75     } else {
76         v = (int64_t)parsehex32(ptr, len);
77     }
78     if (neg)
79         v = -v;
80     return(v);
81 }
82
83 static caps_label_t
84 caps_find_label(caps_struct_t cs, caps_fid_t fid, const struct caps_label **pcache)
85 {
86     caps_label_t label;
87
88     if ((label = *pcache) != NULL) {
89         if (label->fid == fid)
90             return(label);
91         ++label;
92         if (label->fid == fid && label->offset >= 0) {
93             *pcache = label;
94             return(label);
95         }
96         --label;
97         if (label != cs->labels) {
98             --label;
99             if (label->fid == fid && label->offset >= 0) {
100                 *pcache = label;
101                 return(label);
102             }
103         }
104     }
105     for (label = cs->labels; label->offset >= 0; ++label) {
106         if (label->fid == fid) {
107             *pcache = label;
108             return(label);
109         }
110     }
111     return(NULL);
112 }
113
114 /*
115  * Generic structural encoder.  The number of bytes that would be stored in
116  * buf if it were infinitely-sized is returned.
117  */
118 int
119 caps_encode(void *buf, int bytes, void *data, caps_struct_t cs)
120 {
121     struct caps_msgbuf msgbuf;
122
123     caps_init_msgbuf(&msgbuf, buf, bytes);
124     caps_msg_encode_structure(&msgbuf, data, cs);
125     return(msgbuf.index);
126 }
127
128 /*
129  * Encode a structure into a message using the supplied label data.  The
130  * message's index is updated to reflect the actual number of bytes that
131  * would be consumed, even if the message buffer would overflow (but we don't
132  * overflow the buffer, obviously).
133  */
134 void
135 caps_msg_encode_structure(caps_msgbuf_t msg, void *data, caps_struct_t cs)
136 {
137     caps_label_t label;
138     void *ptr;
139
140     caps_msgbuf_printf(msg, "S%s{", cs->name);
141     for (label = cs->labels; label->offset >= 0; ++label) {
142         if (label != cs->labels)
143             caps_msgbuf_putc(msg, ',');
144         caps_msgbuf_printf(msg, "F%x", label->fid);
145         ptr = (char *)data + label->offset;
146         if (label->nary > 1)
147             caps_msg_encode_array(msg, ptr, label);
148         else if (label->csinfo)
149             caps_msg_encode_structure(msg, ptr, label->csinfo);
150         else
151             caps_msg_encode_data(msg, ptr, label->type, label->size);
152     }
153     caps_msgbuf_putc(msg, '}');
154 }
155
156 void
157 caps_msg_encode_array(caps_msgbuf_t msg, void *data, caps_label_t label)
158 {
159     int i;
160     void *ptr;
161
162     caps_msgbuf_printf(msg, "A%x{", label->nary);
163     for (i = 0; i < label->nary; ++i) {
164         ptr = (char *)data + i *
165                 ((label->type & CAPS_OPF_PTR) ? sizeof(void *) : label->size);
166         if (label->csinfo)
167             caps_msg_encode_structure(msg, ptr, label->csinfo);
168         else
169             caps_msg_encode_data(msg, ptr, label->type, label->size);
170     }
171     caps_msgbuf_putc(msg, '}');
172 }
173
174 void
175 caps_msg_encode_data(caps_msgbuf_t msg, void *data, int type, int size)
176 {
177     int i;
178     u_int8_t c;
179
180     switch(type) {
181     case CAPS_OP_INT_T:
182         switch(size) {
183         case 1:
184             if (*(int8_t *)data < 0)
185                 caps_msgbuf_printf(msg, "D-%x", -*(int8_t *)data);
186             else
187                 caps_msgbuf_printf(msg, "D%x", *(u_int8_t *)data);
188             break;
189         case 2:
190             if (*(int16_t *)data < 0)
191                 caps_msgbuf_printf(msg, "D%x", -*(int16_t *)data);
192             else
193                 caps_msgbuf_printf(msg, "D%x", *(u_int16_t *)data);
194             break;
195         case 4:
196             if (*(int32_t *)data < 0)
197                 caps_msgbuf_printf(msg, "D%x", -*(int32_t *)data);
198             else
199                 caps_msgbuf_printf(msg, "D%x", *(u_int32_t *)data);
200             break;
201         case 8:
202             if (*(int64_t *)data < 0)
203                 caps_msgbuf_printf(msg, "D%llx", -*(int64_t *)data);
204             else
205                 caps_msgbuf_printf(msg, "D%llx", *(u_int64_t *)data);
206             break;
207         default:
208             caps_msgbuf_putc(msg, 'D');
209             caps_msgbuf_putc(msg, '?');
210             break;
211         }
212         break;
213     case CAPS_OP_UINT_T:
214         switch(size) {
215         case 1:
216             caps_msgbuf_printf(msg, "D%x", *(u_int8_t *)data);
217             break;
218         case 2:
219             caps_msgbuf_printf(msg, "D%x", *(u_int16_t *)data);
220             break;
221         case 4:
222             caps_msgbuf_printf(msg, "D%x", *(u_int32_t *)data);
223             break;
224         case 8:
225             caps_msgbuf_printf(msg, "D%llx", *(u_int64_t *)data);
226             break;
227         default:
228             caps_msgbuf_putc(msg, 'D');
229             caps_msgbuf_putc(msg, '?');
230             break;
231         }
232         break;
233     case CAPS_OP_STRPTR_T:
234         data = *(void **)data;
235         if (data == NULL) {
236             caps_msgbuf_printf(msg, "D");        /* represents NULL */
237             break;
238         }
239         if (size == 0)
240             size = 0x7FFFFFFF;
241         /* fall through, size is the 'limit' */
242     case CAPS_OP_STRBUF_T:
243         caps_msgbuf_putc(msg, 'D');
244         caps_msgbuf_putc(msg, '"');     /* string designator */
245         for (i = 0; i < size && (c = ((u_int8_t *)data)[i]) != 0; ++i) {
246             if ((c >= 'a' && c <= 'z') ||
247                 (c >= 'A' && c <= 'Z') ||
248                 (c >= '0' && c <= '9') ||
249                 c == '_' || c == '.' || c == '/' || c == '+' || c == '-'
250             ) {
251                 caps_msgbuf_putc(msg, c);
252             } else {
253                 caps_msgbuf_printf(msg, "%%%02x", (int)c);
254             }
255         }
256         caps_msgbuf_putc(msg, '"');
257         break;
258     case CAPS_OP_OPAQUE_T:
259         caps_msgbuf_putc(msg, 'D');
260         caps_msgbuf_putc(msg, '"');
261         for (i = 0; i < size; ++i) {
262             c = ((u_int8_t *)data)[i];
263             if ((c >= 'a' && c <= 'z') ||
264                 (c >= 'A' && c <= 'Z') ||
265                 (c >= '0' && c <= '9') ||
266                 c == '_' || c == '.' || c == '/' || c == '+' || c == '-'
267             ) {
268                 caps_msgbuf_putc(msg, c);
269             } else {
270                 caps_msgbuf_printf(msg, "%%%02x", (int)c);
271             }
272         }
273         caps_msgbuf_putc(msg, '"');
274         break;
275     default:
276         caps_msgbuf_putc(msg, 'D');
277         caps_msgbuf_putc(msg, '?');
278         break;
279     }
280 }
281
282 /*
283  * Generic structural decoder.  The number of bytes that were decoded from
284  * the buffer are returned, the structure is populated, and the error code
285  * is set unconditionally as a side effect.
286  */
287 int
288 caps_decode(const void *buf, int bytes, void *data, caps_struct_t cs, int *error)
289 {
290     struct caps_msgbuf msgbuf;
291     u_int8_t c;
292     u_int8_t *ptr;
293     int len;
294
295     caps_init_msgbuf(&msgbuf, (void *)buf, bytes);
296     while ((c = caps_msgbuf_getclass(&msgbuf, &ptr, &len)) != 0) {
297         if (c == 'S' && len == strlen(cs->name) &&
298             strncmp(ptr, cs->name, len) == 0
299         ) {
300             caps_msg_decode_structure(&msgbuf, data, cs);
301             *error = msgbuf.error;
302             return(msgbuf.index);
303         }
304         /*
305          * Skip substructures.
306          */
307         if (c == '{') {
308             caps_msgbuf_error(&msgbuf, 0, 1);
309             caps_msg_decode_structure(&msgbuf, NULL, NULL);
310         }
311     }
312     if (msgbuf.error == 0)
313         *error = ENOENT;
314     *error = msgbuf.error;
315     return(msgbuf.index);
316 }
317
318 /*
319  * Decode a message buffer into a structure, return the number of bytes
320  * chomped and set *error to 0 on success, or an error code on failure.
321  * The 'Sname' has already been snarfed.  We are responsible for snarfing
322  * the '{'.
323  *
324  * Note that the structural specification, cs, may be NULL, indicating and
325  * unknown structure which causes us to skip the structure.
326  */
327 void
328 caps_msg_decode_structure(caps_msgbuf_t msg, void *data, caps_struct_t cs)
329 {
330     caps_label_t label;
331     caps_label_t cache;
332     u_int8_t *ptr;
333     int len;
334     char c;
335
336     cache = NULL;
337
338     /*
339      * A structure must contain an open brace
340      */
341     if (caps_msgbuf_getc(msg) != '{') {
342         caps_msgbuf_error(msg, EINVAL, 1);
343         return;
344     }
345
346     /*
347      * Parse data elements within the structure
348      */
349     do {
350         /*
351          * Parse class info for the next element.  Note that the label
352          * specification may be NULL.
353          */
354         label = NULL;
355         while ((c = caps_msgbuf_getclass(msg, &ptr, &len)) != 0) {
356             switch(c) {
357             case 'F':
358                 label = caps_find_label(cs, parsehex32(ptr, len), &cache);
359                 continue;
360             case 'A':
361                 caps_msg_decode_array(msg, 
362                                         (char *)data + label->offset,
363                                         parsehex32(ptr, len), 
364                                         label);
365                 continue;
366             case 'S':
367                 if (label && label->csinfo &&
368                     strlen(label->csinfo->name) == len &&
369                     strncmp(label->csinfo->name, ptr, len) == 0
370                 ) {
371                     caps_msg_decode_structure(msg, 
372                                         (char *)data + label->offset, 
373                                         label->csinfo);
374                 } else {
375                     caps_msg_decode_structure(msg, NULL, NULL);
376                 }
377                 continue;
378             case 'D':
379                 if (label) {
380                     caps_msg_decode_data(ptr, len,
381                                         (char *)data + label->offset,
382                                         label->type,
383                                         label->size);
384                 }
385                 continue;
386             case '{':
387                 /*
388                  * This case occurs when we previously hit an unknown class
389                  * which has a sub-structure associated with it.  Parseskip
390                  * the sub-structure.
391                  */
392                 caps_msgbuf_error(msg, 0, 1);
393                 caps_msg_decode_structure(msg, NULL, NULL);
394                 continue;
395             case '}':
396             case ',':
397                 break;
398             default:
399                 /* unknown classes are ignored */
400                 continue;
401             }
402             break;
403         }
404     } while (c == ',');
405
406     /*
407      * A structure must end with a close brace
408      */
409     if (c != '}')
410         caps_msgbuf_error(msg, EINVAL, 1);
411 }
412
413 void
414 caps_msg_decode_array(caps_msgbuf_t msg, void *data, int nary, caps_label_t label)
415 {
416     int i;
417     char c;
418     int len;
419     u_int8_t *ptr;
420
421     c = 0;
422
423     /*
424      * An array must contain an open brace
425      */
426     if (caps_msgbuf_getc(msg) != '{') {
427         caps_msgbuf_error(msg, EINVAL, 1);
428         return;
429     }
430     for (i = 0; i < nary && (label == NULL || i < label->nary); ++i) {
431         while ((c = caps_msgbuf_getclass(msg, &ptr, &len)) != 0) {
432             switch(c) {
433             case 'F':
434                 /* a field id for an array element is not expected */
435                 continue;
436             case 'A':
437                 /* nested arrays are not yet supported */
438                 continue;
439             case 'S':
440                 if (label && label->csinfo && 
441                     strlen(label->csinfo->name) == len &&
442                     strncmp(label->csinfo->name, ptr, len) == 0
443                 ) {
444                     caps_msg_decode_structure(msg, data, label->csinfo);
445                 } else {
446                     caps_msg_decode_structure(msg, NULL, NULL);
447                 }
448                 continue;
449             case 'D':
450                 if (label) {
451                     caps_msg_decode_data(ptr, len, data, 
452                                         label->type, label->size);
453                 }
454                 continue;
455             case '{':
456                 /*
457                  * This case occurs when we previously hit an unknown class
458                  * which has a sub-structure associated with it.  Parseskip
459                  * the sub-structure.
460                  */
461                 caps_msgbuf_error(msg, 0, 1);
462                 caps_msg_decode_structure(msg, NULL, NULL);
463                 continue;
464             case '}':
465             case ',':
466                 break;
467             default:
468                 /* unknown classes are ignored */
469                 continue;
470             }
471             break;
472         }
473
474         if (label) { 
475             data = (char *)data +
476                 ((label->type & CAPS_OPF_PTR) ? sizeof(void *) : label->size);
477         }
478
479         /*
480          * I really expected a comma here
481          */
482         if (c != ',') {
483             caps_msgbuf_error(msg, EINVAL, 0);
484             break;
485         }
486     }
487
488     /*
489      * Our array was too small, exhaust any remaining elements
490      */
491     for (; i < nary; ++i) {
492         while ((c = caps_msgbuf_getclass(msg, &ptr, &len)) != 0) {
493             switch(c) {
494             case 'S':
495                 caps_msg_decode_structure(msg, NULL, NULL);
496                 continue;
497             case 'D':
498                 /* data is embedded, no additional decoding needed to skip */
499                 continue;
500             case '{':
501                 caps_msgbuf_error(msg, 0, 1);
502                 caps_msg_decode_structure(msg, NULL, NULL);
503                 continue;
504             case '}':
505             case ',':
506                 break;
507             default:
508                 /* unknown classes are ignored */
509                 continue;
510             }
511             break;
512         }
513         if (c != ',') {
514             caps_msgbuf_error(msg, EINVAL, 0);
515             break;
516         }
517     }
518
519     /*
520      * Finish up.  Note degenerate case (c not loaded) if nary is 0
521      */
522     if (nary == 0)
523         c = caps_msgbuf_getc(msg);
524     if (c != '}')
525         caps_msgbuf_error(msg, EINVAL, 1);
526 }
527
528 void
529 caps_msg_decode_data(char *ptr, int len, void *data, int type, int size)
530 {
531     int i;
532     int j;
533
534     switch(type) {
535     case CAPS_OP_INT_T:
536     case CAPS_OP_UINT_T:
537         switch(size) {
538         case 1:
539             *(int8_t *)data = parsehex32(ptr, len);
540             break;
541         case 2:
542             *(int16_t *)data = parsehex32(ptr, len);
543             break;
544         case 4:
545             *(int32_t *)data = parsehex32(ptr, len);
546             break;
547         case 8:
548             *(int64_t *)data = parsehex64(ptr, len);
549             break;
550         default:
551             /* unknown data type */
552             break;
553         }
554         break;
555     case CAPS_OP_STRPTR_T:
556         /*
557          * Assume NULL if not a quoted string (the actual encoding for NULL
558          * is a completely empty 'D' specification).
559          */
560         if (len < 2 || ptr[0] != '"' || ptr[len-1] != '"') {
561             *(void **)data = NULL;
562             break;
563         }
564         for (i = j = 0; i < len; ++j) {
565             if (ptr[i] == '%') {
566                 i += 3;
567             } else {
568                 ++i;
569             }
570         }
571         if (size == 0 || size > j)
572             size = j + 1;
573         *(void **)data = malloc(size);
574         data = *(void **)data;
575         assert(data != NULL);
576         /* fall through */
577     case CAPS_OP_STRBUF_T:
578     case CAPS_OP_OPAQUE_T:
579         /*
580          * Skip quotes
581          */
582         if (len < 2 || ptr[0] != '"' || ptr[len-1] != '"') {
583             break;
584         }
585         ++ptr;
586         len -= 2;
587
588         /*
589          * Parse the contents of the string
590          */
591         for (i = j = 0; i < len && j < size; ++j) {
592             if (ptr[i] == '%') {
593                 if (i + 2 < len) {
594                     ((char *)data)[j] = parsehex32(ptr + 1, 2);
595                     i += 3;
596                 } else {
597                     /* XXX error */
598                     i = len;
599                 }
600             } else {
601                 ((char *)data)[j] = ptr[i];
602                 ++i;
603             }
604         }
605         if (type == CAPS_OP_OPAQUE_T) {
606             if (j < size)
607                 bzero((char *)data + j, size - j);
608         } else {
609             if (j < size)
610                 ((char *)data)[j] = 0;
611             else if (size)
612                 ((char *)data)[size - 1] = 0;   /* XXX error */
613         }
614         break;
615     default:
616         break;
617     }
618 }
619
620 /*
621  * Free string pointers dynamically allocated by caps_msg_decode_structure().
622  */
623 void
624 caps_struct_free_pointers(void *data, caps_struct_t cs)
625 {
626     caps_label_t label;
627     void *ptr;
628
629     for (label = cs->labels; label->offset >= 0; ++label) {
630         ptr = (char *)data + label->offset;
631         if (label->nary > 1) {
632             caps_array_free_pointers(ptr, label);
633         } else if (label->csinfo) {
634             caps_struct_free_pointers(ptr, label->csinfo);
635         } else if (label->type & CAPS_OPF_PTR) {
636             if (*(void **)ptr) {
637                 free(*(void **)ptr);
638                 *(void **)ptr = NULL;
639             }
640         }
641     }
642 }
643
644 void
645 caps_array_free_pointers(void *data, caps_label_t label)
646 {
647     int i;
648
649     for (i = 0; i < label->nary; ++i) {
650         if (label->csinfo) {
651             caps_struct_free_pointers(data, label->csinfo);
652         } else if (label->type & CAPS_OPF_PTR) {
653             if (*(void **)data) {
654                 free(*(void **)data);
655                 *(void **)data = NULL;
656             }
657         }
658         data = (char *)data + 
659             ((label->type & CAPS_OPF_PTR) ? sizeof(void *) : label->size);
660     }
661 }