Pre-2.0 release: Sync with HAMMER 64 - NFS and cross-device link fixes.
[dragonfly.git] / lib / libcaps / caps_struct.c
blob56f223fa56a4a621269409724746b93dd1618e30
1 /*
2 * Copyright (c) 2003 Matthew Dillon <dillon@backplane.com>
3 * All rights reserved.
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.
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.
26 * $DragonFly: src/lib/libcaps/caps_struct.c,v 1.1 2004/03/07 23:36:44 dillon Exp $
29 #include "defs.h"
31 static
32 int32_t
33 parsehex32(const u_int8_t *ptr, int len)
35 int neg = 0;
36 int32_t v = 0;
37 u_int8_t c;
39 if (len && ptr[0] == '-') {
40 neg = 1;
41 --len;
42 ++ptr;
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;
54 if (neg)
55 v = -v;
56 return(v);
59 static
60 int64_t
61 parsehex64(const u_int8_t *ptr, int len)
63 int neg = 0;
64 int64_t v;
66 if (len && ptr[0] == '-') {
67 neg = 1;
68 --len;
69 ++ptr;
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);
78 if (neg)
79 v = -v;
80 return(v);
83 static caps_label_t
84 caps_find_label(caps_struct_t cs, caps_fid_t fid, const struct caps_label **pcache)
86 caps_label_t label;
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);
96 --label;
97 if (label != cs->labels) {
98 --label;
99 if (label->fid == fid && label->offset >= 0) {
100 *pcache = label;
101 return(label);
105 for (label = cs->labels; label->offset >= 0; ++label) {
106 if (label->fid == fid) {
107 *pcache = label;
108 return(label);
111 return(NULL);
115 * Generic structural encoder. The number of bytes that would be stored in
116 * buf if it were infinitely-sized is returned.
119 caps_encode(void *buf, int bytes, void *data, caps_struct_t cs)
121 struct caps_msgbuf msgbuf;
123 caps_init_msgbuf(&msgbuf, buf, bytes);
124 caps_msg_encode_structure(&msgbuf, data, cs);
125 return(msgbuf.index);
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).
134 void
135 caps_msg_encode_structure(caps_msgbuf_t msg, void *data, caps_struct_t cs)
137 caps_label_t label;
138 void *ptr;
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);
153 caps_msgbuf_putc(msg, '}');
156 void
157 caps_msg_encode_array(caps_msgbuf_t msg, void *data, caps_label_t label)
159 int i;
160 void *ptr;
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);
171 caps_msgbuf_putc(msg, '}');
174 void
175 caps_msg_encode_data(caps_msgbuf_t msg, void *data, int type, int size)
177 int i;
178 u_int8_t c;
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;
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;
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;
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 == '-'
251 caps_msgbuf_putc(msg, c);
252 } else {
253 caps_msgbuf_printf(msg, "%%%02x", (int)c);
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 == '-'
268 caps_msgbuf_putc(msg, c);
269 } else {
270 caps_msgbuf_printf(msg, "%%%02x", (int)c);
273 caps_msgbuf_putc(msg, '"');
274 break;
275 default:
276 caps_msgbuf_putc(msg, 'D');
277 caps_msgbuf_putc(msg, '?');
278 break;
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.
288 caps_decode(const void *buf, int bytes, void *data, caps_struct_t cs, int *error)
290 struct caps_msgbuf msgbuf;
291 u_int8_t c;
292 u_int8_t *ptr;
293 int len;
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
300 caps_msg_decode_structure(&msgbuf, data, cs);
301 *error = msgbuf.error;
302 return(msgbuf.index);
305 * Skip substructures.
307 if (c == '{') {
308 caps_msgbuf_error(&msgbuf, 0, 1);
309 caps_msg_decode_structure(&msgbuf, NULL, NULL);
312 if (msgbuf.error == 0)
313 *error = ENOENT;
314 *error = msgbuf.error;
315 return(msgbuf.index);
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 '{'.
324 * Note that the structural specification, cs, may be NULL, indicating and
325 * unknown structure which causes us to skip the structure.
327 void
328 caps_msg_decode_structure(caps_msgbuf_t msg, void *data, caps_struct_t cs)
330 caps_label_t label;
331 caps_label_t cache;
332 u_int8_t *ptr;
333 int len;
334 char c;
336 cache = NULL;
339 * A structure must contain an open brace
341 if (caps_msgbuf_getc(msg) != '{') {
342 caps_msgbuf_error(msg, EINVAL, 1);
343 return;
347 * Parse data elements within the structure
349 do {
351 * Parse class info for the next element. Note that the label
352 * specification may be NULL.
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
371 caps_msg_decode_structure(msg,
372 (char *)data + label->offset,
373 label->csinfo);
374 } else {
375 caps_msg_decode_structure(msg, NULL, NULL);
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);
385 continue;
386 case '{':
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.
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;
402 break;
404 } while (c == ',');
407 * A structure must end with a close brace
409 if (c != '}')
410 caps_msgbuf_error(msg, EINVAL, 1);
413 void
414 caps_msg_decode_array(caps_msgbuf_t msg, void *data, int nary, caps_label_t label)
416 int i;
417 char c;
418 int len;
419 u_int8_t *ptr;
421 c = 0;
424 * An array must contain an open brace
426 if (caps_msgbuf_getc(msg) != '{') {
427 caps_msgbuf_error(msg, EINVAL, 1);
428 return;
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
444 caps_msg_decode_structure(msg, data, label->csinfo);
445 } else {
446 caps_msg_decode_structure(msg, NULL, NULL);
448 continue;
449 case 'D':
450 if (label) {
451 caps_msg_decode_data(ptr, len, data,
452 label->type, label->size);
454 continue;
455 case '{':
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.
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;
471 break;
474 if (label) {
475 data = (char *)data +
476 ((label->type & CAPS_OPF_PTR) ? sizeof(void *) : label->size);
480 * I really expected a comma here
482 if (c != ',') {
483 caps_msgbuf_error(msg, EINVAL, 0);
484 break;
489 * Our array was too small, exhaust any remaining elements
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;
511 break;
513 if (c != ',') {
514 caps_msgbuf_error(msg, EINVAL, 0);
515 break;
520 * Finish up. Note degenerate case (c not loaded) if nary is 0
522 if (nary == 0)
523 c = caps_msgbuf_getc(msg);
524 if (c != '}')
525 caps_msgbuf_error(msg, EINVAL, 1);
528 void
529 caps_msg_decode_data(char *ptr, int len, void *data, int type, int size)
531 int i;
532 int j;
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;
554 break;
555 case CAPS_OP_STRPTR_T:
557 * Assume NULL if not a quoted string (the actual encoding for NULL
558 * is a completely empty 'D' specification).
560 if (len < 2 || ptr[0] != '"' || ptr[len-1] != '"') {
561 *(void **)data = NULL;
562 break;
564 for (i = j = 0; i < len; ++j) {
565 if (ptr[i] == '%') {
566 i += 3;
567 } else {
568 ++i;
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:
580 * Skip quotes
582 if (len < 2 || ptr[0] != '"' || ptr[len-1] != '"') {
583 break;
585 ++ptr;
586 len -= 2;
589 * Parse the contents of the string
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;
600 } else {
601 ((char *)data)[j] = ptr[i];
602 ++i;
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 */
614 break;
615 default:
616 break;
621 * Free string pointers dynamically allocated by caps_msg_decode_structure().
623 void
624 caps_struct_free_pointers(void *data, caps_struct_t cs)
626 caps_label_t label;
627 void *ptr;
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;
644 void
645 caps_array_free_pointers(void *data, caps_label_t label)
647 int i;
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;
658 data = (char *)data +
659 ((label->type & CAPS_OPF_PTR) ? sizeof(void *) : label->size);