1 | /* $NetBSD: prop_data.c,v 1.14 2009/01/25 06:59:35 cyber Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2006 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Jason R. Thorpe. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | #include <prop/prop_data.h> |
33 | #include "prop_object_impl.h" |
34 | |
35 | #if defined(_KERNEL) |
36 | #include <sys/systm.h> |
37 | #elif defined(_STANDALONE) |
38 | #include <sys/param.h> |
39 | #include <lib/libkern/libkern.h> |
40 | #else |
41 | #include <errno.h> |
42 | #include <limits.h> |
43 | #include <stdlib.h> |
44 | #endif |
45 | |
46 | struct _prop_data { |
47 | struct _prop_object pd_obj; |
48 | union { |
49 | void * pdu_mutable; |
50 | const void * pdu_immutable; |
51 | } pd_un; |
52 | #define pd_mutable pd_un.pdu_mutable |
53 | #define pd_immutable pd_un.pdu_immutable |
54 | size_t pd_size; |
55 | int pd_flags; |
56 | }; |
57 | |
58 | #define PD_F_NOCOPY 0x01 |
59 | |
60 | _PROP_POOL_INIT(_prop_data_pool, sizeof(struct _prop_data), "propdata" ) |
61 | |
62 | _PROP_MALLOC_DEFINE(M_PROP_DATA, "prop data" , |
63 | "property data container object" ) |
64 | |
65 | static _prop_object_free_rv_t |
66 | _prop_data_free(prop_stack_t, prop_object_t *); |
67 | static bool _prop_data_externalize( |
68 | struct _prop_object_externalize_context *, |
69 | void *); |
70 | static _prop_object_equals_rv_t |
71 | _prop_data_equals(prop_object_t, prop_object_t, |
72 | void **, void **, |
73 | prop_object_t *, prop_object_t *); |
74 | |
75 | static const struct _prop_object_type _prop_object_type_data = { |
76 | .pot_type = PROP_TYPE_DATA, |
77 | .pot_free = _prop_data_free, |
78 | .pot_extern = _prop_data_externalize, |
79 | .pot_equals = _prop_data_equals, |
80 | }; |
81 | |
82 | #define prop_object_is_data(x) \ |
83 | ((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_data) |
84 | |
85 | /* ARGSUSED */ |
86 | static _prop_object_free_rv_t |
87 | _prop_data_free(prop_stack_t stack, prop_object_t *obj) |
88 | { |
89 | prop_data_t pd = *obj; |
90 | |
91 | if ((pd->pd_flags & PD_F_NOCOPY) == 0 && pd->pd_mutable != NULL) |
92 | _PROP_FREE(pd->pd_mutable, M_PROP_DATA); |
93 | _PROP_POOL_PUT(_prop_data_pool, pd); |
94 | |
95 | return (_PROP_OBJECT_FREE_DONE); |
96 | } |
97 | |
98 | static const char _prop_data_base64[] = |
99 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ; |
100 | static const char _prop_data_pad64 = '='; |
101 | |
102 | static bool |
103 | _prop_data_externalize(struct _prop_object_externalize_context *ctx, void *v) |
104 | { |
105 | prop_data_t pd = v; |
106 | size_t i, srclen; |
107 | const uint8_t *src; |
108 | uint8_t output[4]; |
109 | uint8_t input[3]; |
110 | |
111 | if (pd->pd_size == 0) |
112 | return (_prop_object_externalize_empty_tag(ctx, "data" )); |
113 | |
114 | if (_prop_object_externalize_start_tag(ctx, "data" ) == false) |
115 | return (false); |
116 | |
117 | for (src = pd->pd_immutable, srclen = pd->pd_size; |
118 | srclen > 2; srclen -= 3) { |
119 | input[0] = *src++; |
120 | input[1] = *src++; |
121 | input[2] = *src++; |
122 | |
123 | output[0] = (uint32_t)input[0] >> 2; |
124 | output[1] = ((uint32_t)(input[0] & 0x03) << 4) + |
125 | ((uint32_t)input[1] >> 4); |
126 | output[2] = ((uint32_t)(input[1] & 0x0f) << 2) + |
127 | ((uint32_t)input[2] >> 6); |
128 | output[3] = input[2] & 0x3f; |
129 | _PROP_ASSERT(output[0] < 64); |
130 | _PROP_ASSERT(output[1] < 64); |
131 | _PROP_ASSERT(output[2] < 64); |
132 | _PROP_ASSERT(output[3] < 64); |
133 | |
134 | if (_prop_object_externalize_append_char(ctx, |
135 | _prop_data_base64[output[0]]) == false || |
136 | _prop_object_externalize_append_char(ctx, |
137 | _prop_data_base64[output[1]]) == false || |
138 | _prop_object_externalize_append_char(ctx, |
139 | _prop_data_base64[output[2]]) == false || |
140 | _prop_object_externalize_append_char(ctx, |
141 | _prop_data_base64[output[3]]) == false) |
142 | return (false); |
143 | } |
144 | |
145 | if (srclen != 0) { |
146 | input[0] = input[1] = input[2] = '\0'; |
147 | for (i = 0; i < srclen; i++) |
148 | input[i] = *src++; |
149 | |
150 | output[0] = (uint32_t)input[0] >> 2; |
151 | output[1] = ((uint32_t)(input[0] & 0x03) << 4) + |
152 | ((uint32_t)input[1] >> 4); |
153 | output[2] = ((uint32_t)(input[1] & 0x0f) << 2) + |
154 | ((uint32_t)input[2] >> 6); |
155 | _PROP_ASSERT(output[0] < 64); |
156 | _PROP_ASSERT(output[1] < 64); |
157 | _PROP_ASSERT(output[2] < 64); |
158 | |
159 | if (_prop_object_externalize_append_char(ctx, |
160 | _prop_data_base64[output[0]]) == false || |
161 | _prop_object_externalize_append_char(ctx, |
162 | _prop_data_base64[output[1]]) == false || |
163 | _prop_object_externalize_append_char(ctx, |
164 | srclen == 1 ? _prop_data_pad64 |
165 | : _prop_data_base64[output[2]]) == false || |
166 | _prop_object_externalize_append_char(ctx, |
167 | _prop_data_pad64) == false) |
168 | return (false); |
169 | } |
170 | |
171 | if (_prop_object_externalize_end_tag(ctx, "data" ) == false) |
172 | return (false); |
173 | |
174 | return (true); |
175 | } |
176 | |
177 | /* ARGSUSED */ |
178 | static _prop_object_equals_rv_t |
179 | _prop_data_equals(prop_object_t v1, prop_object_t v2, |
180 | void **stored_pointer1, void **stored_pointer2, |
181 | prop_object_t *next_obj1, prop_object_t *next_obj2) |
182 | { |
183 | prop_data_t pd1 = v1; |
184 | prop_data_t pd2 = v2; |
185 | |
186 | if (pd1 == pd2) |
187 | return (_PROP_OBJECT_EQUALS_TRUE); |
188 | if (pd1->pd_size != pd2->pd_size) |
189 | return (_PROP_OBJECT_EQUALS_FALSE); |
190 | if (pd1->pd_size == 0) { |
191 | _PROP_ASSERT(pd1->pd_immutable == NULL); |
192 | _PROP_ASSERT(pd2->pd_immutable == NULL); |
193 | return (_PROP_OBJECT_EQUALS_TRUE); |
194 | } |
195 | if (memcmp(pd1->pd_immutable, pd2->pd_immutable, pd1->pd_size) == 0) |
196 | return _PROP_OBJECT_EQUALS_TRUE; |
197 | else |
198 | return _PROP_OBJECT_EQUALS_FALSE; |
199 | } |
200 | |
201 | static prop_data_t |
202 | _prop_data_alloc(void) |
203 | { |
204 | prop_data_t pd; |
205 | |
206 | pd = _PROP_POOL_GET(_prop_data_pool); |
207 | if (pd != NULL) { |
208 | _prop_object_init(&pd->pd_obj, &_prop_object_type_data); |
209 | |
210 | pd->pd_mutable = NULL; |
211 | pd->pd_size = 0; |
212 | pd->pd_flags = 0; |
213 | } |
214 | |
215 | return (pd); |
216 | } |
217 | |
218 | /* |
219 | * prop_data_create_data -- |
220 | * Create a data container that contains a copy of the data. |
221 | */ |
222 | prop_data_t |
223 | prop_data_create_data(const void *v, size_t size) |
224 | { |
225 | prop_data_t pd; |
226 | void *nv; |
227 | |
228 | pd = _prop_data_alloc(); |
229 | if (pd != NULL && size != 0) { |
230 | nv = _PROP_MALLOC(size, M_PROP_DATA); |
231 | if (nv == NULL) { |
232 | prop_object_release(pd); |
233 | return (NULL); |
234 | } |
235 | memcpy(nv, v, size); |
236 | pd->pd_mutable = nv; |
237 | pd->pd_size = size; |
238 | } |
239 | return (pd); |
240 | } |
241 | |
242 | /* |
243 | * prop_data_create_data_nocopy -- |
244 | * Create an immutable data container that contains a refrence to the |
245 | * provided external data. |
246 | */ |
247 | prop_data_t |
248 | prop_data_create_data_nocopy(const void *v, size_t size) |
249 | { |
250 | prop_data_t pd; |
251 | |
252 | pd = _prop_data_alloc(); |
253 | if (pd != NULL) { |
254 | pd->pd_immutable = v; |
255 | pd->pd_size = size; |
256 | pd->pd_flags |= PD_F_NOCOPY; |
257 | } |
258 | return (pd); |
259 | } |
260 | |
261 | /* |
262 | * prop_data_copy -- |
263 | * Copy a data container. If the original data is external, then |
264 | * the copy is also references the same external data. |
265 | */ |
266 | prop_data_t |
267 | prop_data_copy(prop_data_t opd) |
268 | { |
269 | prop_data_t pd; |
270 | |
271 | if (! prop_object_is_data(opd)) |
272 | return (NULL); |
273 | |
274 | pd = _prop_data_alloc(); |
275 | if (pd != NULL) { |
276 | pd->pd_size = opd->pd_size; |
277 | pd->pd_flags = opd->pd_flags; |
278 | if (opd->pd_flags & PD_F_NOCOPY) |
279 | pd->pd_immutable = opd->pd_immutable; |
280 | else if (opd->pd_size != 0) { |
281 | void *nv = _PROP_MALLOC(pd->pd_size, M_PROP_DATA); |
282 | if (nv == NULL) { |
283 | prop_object_release(pd); |
284 | return (NULL); |
285 | } |
286 | memcpy(nv, opd->pd_immutable, opd->pd_size); |
287 | pd->pd_mutable = nv; |
288 | } |
289 | } |
290 | return (pd); |
291 | } |
292 | |
293 | /* |
294 | * prop_data_size -- |
295 | * Return the size of the data. |
296 | */ |
297 | size_t |
298 | prop_data_size(prop_data_t pd) |
299 | { |
300 | |
301 | if (! prop_object_is_data(pd)) |
302 | return (0); |
303 | |
304 | return (pd->pd_size); |
305 | } |
306 | |
307 | /* |
308 | * prop_data_data -- |
309 | * Return a copy of the contents of the data container. |
310 | * The data is allocated with the M_TEMP malloc type. |
311 | * If the data container is empty, NULL is returned. |
312 | */ |
313 | void * |
314 | prop_data_data(prop_data_t pd) |
315 | { |
316 | void *v; |
317 | |
318 | if (! prop_object_is_data(pd)) |
319 | return (NULL); |
320 | |
321 | if (pd->pd_size == 0) { |
322 | _PROP_ASSERT(pd->pd_immutable == NULL); |
323 | return (NULL); |
324 | } |
325 | |
326 | _PROP_ASSERT(pd->pd_immutable != NULL); |
327 | |
328 | v = _PROP_MALLOC(pd->pd_size, M_TEMP); |
329 | if (v != NULL) |
330 | memcpy(v, pd->pd_immutable, pd->pd_size); |
331 | |
332 | return (v); |
333 | } |
334 | |
335 | /* |
336 | * prop_data_data_nocopy -- |
337 | * Return an immutable reference to the contents of the data |
338 | * container. |
339 | */ |
340 | const void * |
341 | prop_data_data_nocopy(prop_data_t pd) |
342 | { |
343 | |
344 | if (! prop_object_is_data(pd)) |
345 | return (NULL); |
346 | |
347 | _PROP_ASSERT((pd->pd_size == 0 && pd->pd_immutable == NULL) || |
348 | (pd->pd_size != 0 && pd->pd_immutable != NULL)); |
349 | |
350 | return (pd->pd_immutable); |
351 | } |
352 | |
353 | /* |
354 | * prop_data_equals -- |
355 | * Return true if two strings are equivalent. |
356 | */ |
357 | bool |
358 | prop_data_equals(prop_data_t pd1, prop_data_t pd2) |
359 | { |
360 | if (!prop_object_is_data(pd1) || !prop_object_is_data(pd2)) |
361 | return (false); |
362 | |
363 | return (prop_object_equals(pd1, pd2)); |
364 | } |
365 | |
366 | /* |
367 | * prop_data_equals_data -- |
368 | * Return true if the contained data is equivalent to the specified |
369 | * external data. |
370 | */ |
371 | bool |
372 | prop_data_equals_data(prop_data_t pd, const void *v, size_t size) |
373 | { |
374 | |
375 | if (! prop_object_is_data(pd)) |
376 | return (false); |
377 | |
378 | if (pd->pd_size != size) |
379 | return (false); |
380 | return (memcmp(pd->pd_immutable, v, size) == 0); |
381 | } |
382 | |
383 | static bool |
384 | _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx, |
385 | uint8_t *target, size_t targsize, size_t *sizep, |
386 | const char **cpp) |
387 | { |
388 | const char *src; |
389 | size_t tarindex; |
390 | int state, ch; |
391 | const char *pos; |
392 | |
393 | state = 0; |
394 | tarindex = 0; |
395 | src = ctx->poic_cp; |
396 | |
397 | for (;;) { |
398 | ch = (unsigned char) *src++; |
399 | if (_PROP_EOF(ch)) |
400 | return (false); |
401 | if (_PROP_ISSPACE(ch)) |
402 | continue; |
403 | if (ch == '<') { |
404 | src--; |
405 | break; |
406 | } |
407 | if (ch == _prop_data_pad64) |
408 | break; |
409 | |
410 | pos = strchr(_prop_data_base64, ch); |
411 | if (pos == NULL) |
412 | return (false); |
413 | |
414 | switch (state) { |
415 | case 0: |
416 | if (target) { |
417 | if (tarindex >= targsize) |
418 | return (false); |
419 | target[tarindex] = |
420 | (uint8_t)((pos - _prop_data_base64) << 2); |
421 | } |
422 | state = 1; |
423 | break; |
424 | |
425 | case 1: |
426 | if (target) { |
427 | if (tarindex + 1 >= targsize) |
428 | return (false); |
429 | target[tarindex] |= |
430 | (uint32_t)(pos - _prop_data_base64) >> 4; |
431 | target[tarindex + 1] = |
432 | (uint8_t)(((pos - _prop_data_base64) & 0xf) |
433 | << 4); |
434 | } |
435 | tarindex++; |
436 | state = 2; |
437 | break; |
438 | |
439 | case 2: |
440 | if (target) { |
441 | if (tarindex + 1 >= targsize) |
442 | return (false); |
443 | target[tarindex] |= |
444 | (uint32_t)(pos - _prop_data_base64) >> 2; |
445 | target[tarindex + 1] = |
446 | (uint8_t)(((pos - _prop_data_base64) |
447 | & 0x3) << 6); |
448 | } |
449 | tarindex++; |
450 | state = 3; |
451 | break; |
452 | |
453 | case 3: |
454 | if (target) { |
455 | if (tarindex >= targsize) |
456 | return (false); |
457 | target[tarindex] |= (uint8_t) |
458 | (pos - _prop_data_base64); |
459 | } |
460 | tarindex++; |
461 | state = 0; |
462 | break; |
463 | |
464 | default: |
465 | _PROP_ASSERT(/*CONSTCOND*/0); |
466 | } |
467 | } |
468 | |
469 | /* |
470 | * We are done decoding the Base64 characters. Let's see if we |
471 | * ended up on a byte boundary and/or with unrecognized trailing |
472 | * characters. |
473 | */ |
474 | if (ch == _prop_data_pad64) { |
475 | ch = (unsigned char) *src; /* src already advanced */ |
476 | if (_PROP_EOF(ch)) |
477 | return (false); |
478 | switch (state) { |
479 | case 0: /* Invalid = in first position */ |
480 | case 1: /* Invalid = in second position */ |
481 | return (false); |
482 | |
483 | case 2: /* Valid, one byte of info */ |
484 | /* Skip whitespace */ |
485 | for (ch = (unsigned char) *src++; |
486 | ch != '<'; ch = (unsigned char) *src++) { |
487 | if (_PROP_EOF(ch)) |
488 | return (false); |
489 | if (!_PROP_ISSPACE(ch)) |
490 | break; |
491 | } |
492 | /* Make sure there is another trailing = */ |
493 | if (ch != _prop_data_pad64) |
494 | return (false); |
495 | ch = (unsigned char) *src; |
496 | /* FALLTHROUGH */ |
497 | |
498 | case 3: /* Valid, two bytes of info */ |
499 | /* |
500 | * We know this char is a =. Is there anything but |
501 | * whitespace after it? |
502 | */ |
503 | for (ch = (unsigned char) *src++; |
504 | ch != '<'; ch = (unsigned char) *src++) { |
505 | if (_PROP_EOF(ch)) |
506 | return (false); |
507 | if (!_PROP_ISSPACE(ch)) |
508 | return (false); |
509 | } |
510 | /* back up to '<' */ |
511 | src--; |
512 | } |
513 | } else { |
514 | /* |
515 | * We ended by seeing the end of the Base64 string. Make |
516 | * sure there are no partial bytes lying around. |
517 | */ |
518 | if (state != 0) |
519 | return (false); |
520 | } |
521 | |
522 | _PROP_ASSERT(*src == '<'); |
523 | if (sizep != NULL) |
524 | *sizep = tarindex; |
525 | if (cpp != NULL) |
526 | *cpp = src; |
527 | |
528 | return (true); |
529 | } |
530 | |
531 | /* |
532 | * _prop_data_internalize -- |
533 | * Parse a <data>...</data> and return the object created from the |
534 | * external representation. |
535 | */ |
536 | |
537 | /* strtoul is used for parsing, enforce. */ |
538 | typedef int PROP_DATA_ASSERT[/* CONSTCOND */sizeof(size_t) == sizeof(unsigned long) ? 1 : -1]; |
539 | |
540 | /* ARGSUSED */ |
541 | bool |
542 | _prop_data_internalize(prop_stack_t stack, prop_object_t *obj, |
543 | struct _prop_object_internalize_context *ctx) |
544 | { |
545 | prop_data_t data; |
546 | uint8_t *buf; |
547 | size_t len, alen; |
548 | |
549 | /* |
550 | * We don't accept empty elements. |
551 | * This actually only checks for the node to be <data/> |
552 | * (Which actually causes another error if found.) |
553 | */ |
554 | if (ctx->poic_is_empty_element) |
555 | return (true); |
556 | |
557 | /* |
558 | * If we got a "size" attribute, get the size of the data blob |
559 | * from that. Otherwise, we have to figure it out from the base64. |
560 | */ |
561 | if (ctx->poic_tagattr != NULL) { |
562 | char *cp; |
563 | |
564 | if (!_PROP_TAGATTR_MATCH(ctx, "size" ) || |
565 | ctx->poic_tagattrval_len == 0) |
566 | return (true); |
567 | |
568 | #ifndef _KERNEL |
569 | errno = 0; |
570 | #endif |
571 | len = strtoul(ctx->poic_tagattrval, &cp, 0); |
572 | #ifndef _KERNEL /* XXX can't check for ERANGE in the kernel */ |
573 | if (len == ULONG_MAX && errno == ERANGE) |
574 | return (true); |
575 | #endif |
576 | if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len) |
577 | return (true); |
578 | _PROP_ASSERT(*cp == '\"'); |
579 | } else if (_prop_data_internalize_decode(ctx, NULL, 0, &len, |
580 | NULL) == false) |
581 | return (true); |
582 | |
583 | /* |
584 | * Always allocate one extra in case we don't land on an even byte |
585 | * boundary during the decode. |
586 | */ |
587 | buf = _PROP_MALLOC(len + 1, M_PROP_DATA); |
588 | if (buf == NULL) |
589 | return (true); |
590 | |
591 | if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen, |
592 | &ctx->poic_cp) == false) { |
593 | _PROP_FREE(buf, M_PROP_DATA); |
594 | return (true); |
595 | } |
596 | if (alen != len) { |
597 | _PROP_FREE(buf, M_PROP_DATA); |
598 | return (true); |
599 | } |
600 | |
601 | if (_prop_object_internalize_find_tag(ctx, "data" , |
602 | _PROP_TAG_TYPE_END) == false) { |
603 | _PROP_FREE(buf, M_PROP_DATA); |
604 | return (true); |
605 | } |
606 | |
607 | data = _prop_data_alloc(); |
608 | if (data == NULL) { |
609 | _PROP_FREE(buf, M_PROP_DATA); |
610 | return (true); |
611 | } |
612 | |
613 | /* |
614 | * Handle alternate type of empty node. |
615 | * XML document could contain open/close tags, yet still be empty. |
616 | */ |
617 | if (alen == 0) { |
618 | _PROP_FREE(buf, M_PROP_DATA); |
619 | data->pd_mutable = NULL; |
620 | } else { |
621 | data->pd_mutable = buf; |
622 | } |
623 | data->pd_size = len; |
624 | |
625 | *obj = data; |
626 | return (true); |
627 | } |
628 | |