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