prop_data.c revision 1.7 1 /* $NetBSD: prop_data.c,v 1.7 2007/08/16 16:28:17 thorpej 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 void _prop_data_free(void *);
73 static bool _prop_data_externalize(
74 struct _prop_object_externalize_context *,
75 void *);
76 static bool _prop_data_equals(void *, void *);
77
78 static const struct _prop_object_type _prop_object_type_data = {
79 .pot_type = PROP_TYPE_DATA,
80 .pot_free = _prop_data_free,
81 .pot_extern = _prop_data_externalize,
82 .pot_equals = _prop_data_equals,
83 };
84
85 #define prop_object_is_data(x) \
86 ((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_data)
87
88 static void
89 _prop_data_free(void *v)
90 {
91 prop_data_t pd = v;
92
93 if ((pd->pd_flags & PD_F_NOCOPY) == 0 && pd->pd_mutable != NULL)
94 _PROP_FREE(pd->pd_mutable, M_PROP_DATA);
95 _PROP_POOL_PUT(_prop_data_pool, v);
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] = ((u_int32_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] = ((u_int32_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 static bool
178 _prop_data_equals(void *v1, void *v2)
179 {
180 prop_data_t pd1 = v1;
181 prop_data_t pd2 = v2;
182
183 if (! (prop_object_is_data(pd1) &&
184 prop_object_is_data(pd2)))
185 return (false);
186
187 if (pd1 == pd2)
188 return (true);
189 if (pd1->pd_size != pd2->pd_size)
190 return (false);
191 if (pd1->pd_size == 0) {
192 _PROP_ASSERT(pd1->pd_immutable == NULL);
193 _PROP_ASSERT(pd2->pd_immutable == NULL);
194 return (true);
195 }
196 return (memcmp(pd1->pd_immutable, pd2->pd_immutable,
197 pd1->pd_size) == 0);
198 }
199
200 static prop_data_t
201 _prop_data_alloc(void)
202 {
203 prop_data_t pd;
204
205 pd = _PROP_POOL_GET(_prop_data_pool);
206 if (pd != NULL) {
207 _prop_object_init(&pd->pd_obj, &_prop_object_type_data);
208
209 pd->pd_mutable = NULL;
210 pd->pd_size = 0;
211 pd->pd_flags = 0;
212 }
213
214 return (pd);
215 }
216
217 /*
218 * prop_data_create_data --
219 * Create a data container that contains a copy of the data.
220 */
221 prop_data_t
222 prop_data_create_data(const void *v, size_t size)
223 {
224 prop_data_t pd;
225 void *nv;
226
227 pd = _prop_data_alloc();
228 if (pd != NULL) {
229 nv = _PROP_MALLOC(size, M_PROP_DATA);
230 if (nv == NULL) {
231 _prop_data_free(pd);
232 return (NULL);
233 }
234 memcpy(nv, v, size);
235 pd->pd_mutable = nv;
236 pd->pd_size = size;
237 }
238 return (pd);
239 }
240
241 /*
242 * prop_data_create_data_nocopy --
243 * Create an immutable data container that contains a refrence to the
244 * provided external data.
245 */
246 prop_data_t
247 prop_data_create_data_nocopy(const void *v, size_t size)
248 {
249 prop_data_t pd;
250
251 pd = _prop_data_alloc();
252 if (pd != NULL) {
253 pd->pd_immutable = v;
254 pd->pd_size = size;
255 pd->pd_flags |= PD_F_NOCOPY;
256 }
257 return (pd);
258 }
259
260 /*
261 * prop_data_copy --
262 * Copy a data container. If the original data is external, then
263 * the copy is also references the same external data.
264 */
265 prop_data_t
266 prop_data_copy(prop_data_t opd)
267 {
268 prop_data_t pd;
269
270 if (! prop_object_is_data(opd))
271 return (NULL);
272
273 pd = _prop_data_alloc();
274 if (pd != NULL) {
275 pd->pd_size = opd->pd_size;
276 pd->pd_flags = opd->pd_flags;
277 if (opd->pd_flags & PD_F_NOCOPY)
278 pd->pd_immutable = opd->pd_immutable;
279 else if (opd->pd_size != 0) {
280 void *nv = _PROP_MALLOC(pd->pd_size, M_PROP_DATA);
281 if (nv == NULL) {
282 _prop_data_free(pd);
283 return (NULL);
284 }
285 memcpy(nv, opd->pd_immutable, opd->pd_size);
286 pd->pd_mutable = nv;
287 }
288 }
289 return (pd);
290 }
291
292 /*
293 * prop_data_size --
294 * Return the size of the data.
295 */
296 size_t
297 prop_data_size(prop_data_t pd)
298 {
299
300 if (! prop_object_is_data(pd))
301 return (0);
302
303 return (pd->pd_size);
304 }
305
306 /*
307 * prop_data_data --
308 * Return a copy of the contents of the data container.
309 * The data is allocated with the M_TEMP malloc type.
310 * If the data container is empty, NULL is returned.
311 */
312 void *
313 prop_data_data(prop_data_t pd)
314 {
315 void *v;
316
317 if (! prop_object_is_data(pd))
318 return (NULL);
319
320 if (pd->pd_size == 0) {
321 _PROP_ASSERT(pd->pd_immutable == NULL);
322 return (NULL);
323 }
324
325 _PROP_ASSERT(pd->pd_immutable != NULL);
326
327 v = _PROP_MALLOC(pd->pd_size, M_TEMP);
328 if (v != NULL)
329 memcpy(v, pd->pd_immutable, pd->pd_size);
330
331 return (v);
332 }
333
334 /*
335 * prop_data_data_nocopy --
336 * Return an immutable reference to the contents of the data
337 * container.
338 */
339 const void *
340 prop_data_data_nocopy(prop_data_t pd)
341 {
342
343 if (! prop_object_is_data(pd))
344 return (NULL);
345
346 _PROP_ASSERT((pd->pd_size == 0 && pd->pd_immutable == NULL) ||
347 (pd->pd_size != 0 && pd->pd_immutable != NULL));
348
349 return (pd->pd_immutable);
350 }
351
352 /*
353 * prop_data_equals --
354 * Return true if two strings are equivalent.
355 */
356 bool
357 prop_data_equals(prop_data_t pd1, prop_data_t pd2)
358 {
359
360 return (_prop_data_equals(pd1, pd2));
361 }
362
363 /*
364 * prop_data_equals_data --
365 * Return true if the contained data is equivalent to the specified
366 * external data.
367 */
368 bool
369 prop_data_equals_data(prop_data_t pd, const void *v, size_t size)
370 {
371
372 if (! prop_object_is_data(pd))
373 return (false);
374
375 if (pd->pd_size != size)
376 return (false);
377 return (memcmp(pd->pd_immutable, v, size) == 0);
378 }
379
380 static bool
381 _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx,
382 uint8_t *target, size_t targsize, size_t *sizep,
383 const char **cpp)
384 {
385 const char *src;
386 size_t tarindex;
387 int state, ch;
388 const char *pos;
389
390 state = 0;
391 tarindex = 0;
392 src = ctx->poic_cp;
393
394 for (;;) {
395 ch = (unsigned char) *src++;
396 if (_PROP_EOF(ch))
397 return (false);
398 if (_PROP_ISSPACE(ch))
399 continue;
400 if (ch == '<') {
401 src--;
402 break;
403 }
404 if (ch == _prop_data_pad64)
405 break;
406
407 pos = strchr(_prop_data_base64, ch);
408 if (pos == NULL)
409 return (false);
410
411 switch (state) {
412 case 0:
413 if (target) {
414 if (tarindex >= targsize)
415 return (false);
416 target[tarindex] =
417 (uint8_t)((pos - _prop_data_base64) << 2);
418 }
419 state = 1;
420 break;
421
422 case 1:
423 if (target) {
424 if (tarindex + 1 >= targsize)
425 return (false);
426 target[tarindex] |=
427 (uint32_t)(pos - _prop_data_base64) >> 4;
428 target[tarindex + 1] =
429 (uint8_t)(((pos - _prop_data_base64) & 0xf)
430 << 4);
431 }
432 tarindex++;
433 state = 2;
434 break;
435
436 case 2:
437 if (target) {
438 if (tarindex + 1 >= targsize)
439 return (false);
440 target[tarindex] |=
441 (uint32_t)(pos - _prop_data_base64) >> 2;
442 target[tarindex + 1] =
443 (uint8_t)(((pos - _prop_data_base64)
444 & 0x3) << 6);
445 }
446 tarindex++;
447 state = 3;
448 break;
449
450 case 3:
451 if (target) {
452 if (tarindex >= targsize)
453 return (false);
454 target[tarindex] |= (uint8_t)
455 (pos - _prop_data_base64);
456 }
457 tarindex++;
458 state = 0;
459 break;
460
461 default:
462 _PROP_ASSERT(/*CONSTCOND*/0);
463 }
464 }
465
466 /*
467 * We are done decoding the Base64 characters. Let's see if we
468 * ended up on a byte boundary and/or with unrecognized trailing
469 * characters.
470 */
471 if (ch == _prop_data_pad64) {
472 ch = (unsigned char) *src; /* src already advanced */
473 if (_PROP_EOF(ch))
474 return (false);
475 switch (state) {
476 case 0: /* Invalid = in first position */
477 case 1: /* Invalid = in second position */
478 return (false);
479
480 case 2: /* Valid, one byte of info */
481 /* Skip whitespace */
482 for (ch = (unsigned char) *src++;
483 ch != '<'; ch = (unsigned char) *src++) {
484 if (_PROP_EOF(ch))
485 return (false);
486 if (!_PROP_ISSPACE(ch))
487 break;
488 }
489 /* Make sure there is another trailing = */
490 if (ch != _prop_data_pad64)
491 return (false);
492 ch = (unsigned char) *src;
493 /* FALLTHROUGH */
494
495 case 3: /* Valid, two bytes of info */
496 /*
497 * We know this char is a =. Is there anything but
498 * whitespace after it?
499 */
500 for (ch = (unsigned char) *src++;
501 ch != '<'; ch = (unsigned char) *src++) {
502 if (_PROP_EOF(ch))
503 return (false);
504 if (!_PROP_ISSPACE(ch))
505 return (false);
506 }
507 /* back up to '<' */
508 src--;
509 }
510 } else {
511 /*
512 * We ended by seeing the end of the Base64 string. Make
513 * sure there are no partial bytes lying around.
514 */
515 if (state != 0)
516 return (false);
517 }
518
519 _PROP_ASSERT(*src == '<');
520 if (sizep != NULL)
521 *sizep = tarindex;
522 if (cpp != NULL)
523 *cpp = src;
524
525 return (true);
526 }
527
528 /*
529 * _prop_data_internalize --
530 * Parse a <data>...</data> and return the object created from the
531 * external representation.
532 */
533 prop_object_t
534 _prop_data_internalize(struct _prop_object_internalize_context *ctx)
535 {
536 prop_data_t data;
537 uint8_t *buf;
538 size_t len, alen;
539
540 /* We don't accept empty elements. */
541 if (ctx->poic_is_empty_element)
542 return (NULL);
543
544 /*
545 * If we got a "size" attribute, get the size of the data blob
546 * from that. Otherwise, we have to figure it out from the base64.
547 */
548 if (ctx->poic_tagattr != NULL) {
549 char *cp;
550
551 if (!_PROP_TAGATTR_MATCH(ctx, "size") ||
552 ctx->poic_tagattrval_len == 0)
553 return (NULL);
554
555 #ifndef _KERNEL
556 errno = 0;
557 #endif
558 /* XXX Assumes size_t and unsigned long are the same size. */
559 len = strtoul(ctx->poic_tagattrval, &cp, 0);
560 #ifndef _KERNEL /* XXX can't check for ERANGE in the kernel */
561 if (len == ULONG_MAX && errno == ERANGE)
562 return (NULL);
563 #endif
564 if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len)
565 return (NULL);
566 _PROP_ASSERT(*cp == '\"');
567 } else if (_prop_data_internalize_decode(ctx, NULL, 0, &len,
568 NULL) == false)
569 return (NULL);
570
571 /*
572 * Always allocate one extra in case we don't land on an even byte
573 * boundary during the decode.
574 */
575 buf = _PROP_MALLOC(len + 1, M_PROP_DATA);
576 if (buf == NULL)
577 return (NULL);
578
579 if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen,
580 &ctx->poic_cp) == false) {
581 _PROP_FREE(buf, M_PROP_DATA);
582 return (NULL);
583 }
584 if (alen != len) {
585 _PROP_FREE(buf, M_PROP_DATA);
586 return (NULL);
587 }
588
589 if (_prop_object_internalize_find_tag(ctx, "data",
590 _PROP_TAG_TYPE_END) == false) {
591 _PROP_FREE(buf, M_PROP_DATA);
592 return (NULL);
593 }
594
595 data = _prop_data_alloc();
596 if (data == NULL) {
597 _PROP_FREE(buf, M_PROP_DATA);
598 return (NULL);
599 }
600
601 data->pd_mutable = buf;
602 data->pd_size = len;
603
604 return (data);
605 }
606