xref: /minix3/common/lib/libprop/prop_data.c (revision 6b6d114a21a24bee45291fa4af8899e108396c4a)
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
_prop_data_free(prop_stack_t stack,prop_object_t * obj)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
_prop_data_externalize(struct _prop_object_externalize_context * ctx,void * v)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
_prop_data_equals(prop_object_t v1,prop_object_t v2,void ** stored_pointer1,void ** stored_pointer2,prop_object_t * next_obj1,prop_object_t * next_obj2)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
_prop_data_alloc(void)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
prop_data_create_data(const void * v,size_t size)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
prop_data_create_data_nocopy(const void * v,size_t size)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
prop_data_copy(prop_data_t opd)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
prop_data_size(prop_data_t pd)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 *
prop_data_data(prop_data_t pd)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 *
prop_data_data_nocopy(prop_data_t pd)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
prop_data_equals(prop_data_t pd1,prop_data_t pd2)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
prop_data_equals_data(prop_data_t pd,const void * v,size_t size)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
_prop_data_internalize_decode(struct _prop_object_internalize_context * ctx,uint8_t * target,size_t targsize,size_t * sizep,const char ** cpp)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
_prop_data_internalize(prop_stack_t stack,prop_object_t * obj,struct _prop_object_internalize_context * ctx)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