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