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