Home | History | Annotate | Line # | Download | only in libprop
prop_object.c revision 1.18
      1 /*	$NetBSD: prop_object.c,v 1.18 2008/01/04 21:35:19 xtraeme Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2006, 2007 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 <sys/simplelock.h>
     40 
     41 #include <prop/prop_object.h>
     42 #include "prop_object_impl.h"
     43 
     44 #if !defined(_KERNEL) && !defined(_STANDALONE)
     45 #include <sys/mman.h>
     46 #include <sys/stat.h>
     47 #include <errno.h>
     48 #include <fcntl.h>
     49 #include <limits.h>
     50 #include <unistd.h>
     51 #endif
     52 
     53 #ifdef _STANDALONE
     54 void *
     55 _prop_standalone_calloc(size_t size)
     56 {
     57 	void *rv;
     58 
     59 	rv = alloc(size);
     60 	if (rv != NULL)
     61 		memset(rv, 0, size);
     62 
     63 	return (rv);
     64 }
     65 
     66 void *
     67 _prop_standalone_realloc(void *v, size_t size)
     68 {
     69 	void *rv;
     70 
     71 	rv = alloc(size);
     72 	if (rv != NULL) {
     73 		memcpy(rv, v, size);	/* XXX */
     74 		dealloc(v, 0);		/* XXX */
     75 	}
     76 
     77 	return (rv);
     78 }
     79 #endif /* _STANDALONE */
     80 
     81 /*
     82  * _prop_object_init --
     83  *	Initialize an object.  Called when sub-classes create
     84  *	an instance.
     85  */
     86 void
     87 _prop_object_init(struct _prop_object *po, const struct _prop_object_type *pot)
     88 {
     89 
     90 	po->po_type = pot;
     91 	po->po_refcnt = 1;
     92 }
     93 
     94 /*
     95  * _prop_object_fini --
     96  *	Finalize an object.  Called when sub-classes destroy
     97  *	an instance.
     98  */
     99 /*ARGSUSED*/
    100 void
    101 _prop_object_fini(struct _prop_object *po _PROP_ARG_UNUSED)
    102 {
    103 	/* Nothing to do, currently. */
    104 }
    105 
    106 /*
    107  * _prop_object_externalize_start_tag --
    108  *	Append an XML-style start tag to the externalize buffer.
    109  */
    110 bool
    111 _prop_object_externalize_start_tag(
    112     struct _prop_object_externalize_context *ctx, const char *tag)
    113 {
    114 	unsigned int i;
    115 
    116 	for (i = 0; i < ctx->poec_depth; i++) {
    117 		if (_prop_object_externalize_append_char(ctx, '\t') == false)
    118 			return (false);
    119 	}
    120 	if (_prop_object_externalize_append_char(ctx, '<') == false ||
    121 	    _prop_object_externalize_append_cstring(ctx, tag) == false ||
    122 	    _prop_object_externalize_append_char(ctx, '>') == false)
    123 		return (false);
    124 
    125 	return (true);
    126 }
    127 
    128 /*
    129  * _prop_object_externalize_end_tag --
    130  *	Append an XML-style end tag to the externalize buffer.
    131  */
    132 bool
    133 _prop_object_externalize_end_tag(
    134     struct _prop_object_externalize_context *ctx, const char *tag)
    135 {
    136 
    137 	if (_prop_object_externalize_append_char(ctx, '<') == false ||
    138 	    _prop_object_externalize_append_char(ctx, '/') == false ||
    139 	    _prop_object_externalize_append_cstring(ctx, tag) == false ||
    140 	    _prop_object_externalize_append_char(ctx, '>') == false ||
    141 	    _prop_object_externalize_append_char(ctx, '\n') == false)
    142 		return (false);
    143 
    144 	return (true);
    145 }
    146 
    147 /*
    148  * _prop_object_externalize_empty_tag --
    149  *	Append an XML-style empty tag to the externalize buffer.
    150  */
    151 bool
    152 _prop_object_externalize_empty_tag(
    153     struct _prop_object_externalize_context *ctx, const char *tag)
    154 {
    155 	unsigned int i;
    156 
    157 	for (i = 0; i < ctx->poec_depth; i++) {
    158 		if (_prop_object_externalize_append_char(ctx, '\t') == false)
    159 			return (false);
    160 	}
    161 
    162 	if (_prop_object_externalize_append_char(ctx, '<') == false ||
    163 	    _prop_object_externalize_append_cstring(ctx, tag) == false ||
    164 	    _prop_object_externalize_append_char(ctx, '/') == false ||
    165 	    _prop_object_externalize_append_char(ctx, '>') == false ||
    166 	    _prop_object_externalize_append_char(ctx, '\n') == false)
    167 	    	return (false);
    168 
    169 	return (true);
    170 }
    171 
    172 /*
    173  * _prop_object_externalize_append_cstring --
    174  *	Append a C string to the externalize buffer.
    175  */
    176 bool
    177 _prop_object_externalize_append_cstring(
    178     struct _prop_object_externalize_context *ctx, const char *cp)
    179 {
    180 
    181 	while (*cp != '\0') {
    182 		if (_prop_object_externalize_append_char(ctx,
    183 						(unsigned char) *cp) == false)
    184 			return (false);
    185 		cp++;
    186 	}
    187 
    188 	return (true);
    189 }
    190 
    191 /*
    192  * _prop_object_externalize_append_encoded_cstring --
    193  *	Append an encoded C string to the externalize buffer.
    194  */
    195 bool
    196 _prop_object_externalize_append_encoded_cstring(
    197     struct _prop_object_externalize_context *ctx, const char *cp)
    198 {
    199 
    200 	while (*cp != '\0') {
    201 		switch (*cp) {
    202 		case '<':
    203 			if (_prop_object_externalize_append_cstring(ctx,
    204 					"&lt;") == false)
    205 				return (false);
    206 			break;
    207 		case '>':
    208 			if (_prop_object_externalize_append_cstring(ctx,
    209 					"&gt;") == false)
    210 				return (false);
    211 			break;
    212 		case '&':
    213 			if (_prop_object_externalize_append_cstring(ctx,
    214 					"&amp;") == false)
    215 				return (false);
    216 			break;
    217 		default:
    218 			if (_prop_object_externalize_append_char(ctx,
    219 					(unsigned char) *cp) == false)
    220 				return (false);
    221 			break;
    222 		}
    223 		cp++;
    224 	}
    225 
    226 	return (true);
    227 }
    228 
    229 #define	BUF_EXPAND		256
    230 
    231 /*
    232  * _prop_object_externalize_append_char --
    233  *	Append a single character to the externalize buffer.
    234  */
    235 bool
    236 _prop_object_externalize_append_char(
    237     struct _prop_object_externalize_context *ctx, unsigned char c)
    238 {
    239 
    240 	_PROP_ASSERT(ctx->poec_capacity != 0);
    241 	_PROP_ASSERT(ctx->poec_buf != NULL);
    242 	_PROP_ASSERT(ctx->poec_len <= ctx->poec_capacity);
    243 
    244 	if (ctx->poec_len == ctx->poec_capacity) {
    245 		char *cp = _PROP_REALLOC(ctx->poec_buf,
    246 					 ctx->poec_capacity + BUF_EXPAND,
    247 					 M_TEMP);
    248 		if (cp == NULL)
    249 			return (false);
    250 		ctx->poec_capacity = ctx->poec_capacity + BUF_EXPAND;
    251 		ctx->poec_buf = cp;
    252 	}
    253 
    254 	ctx->poec_buf[ctx->poec_len++] = c;
    255 
    256 	return (true);
    257 }
    258 
    259 /*
    260  * _prop_object_externalize_header --
    261  *	Append the standard XML header to the externalize buffer.
    262  */
    263 bool
    264 _prop_object_externalize_header(struct _prop_object_externalize_context *ctx)
    265 {
    266 	static const char _plist_xml_header[] =
    267 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
    268 "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n";
    269 
    270 	if (_prop_object_externalize_append_cstring(ctx,
    271 						 _plist_xml_header) == false ||
    272 	    _prop_object_externalize_start_tag(ctx,
    273 				       "plist version=\"1.0\"") == false ||
    274 	    _prop_object_externalize_append_char(ctx, '\n') == false)
    275 		return (false);
    276 
    277 	return (true);
    278 }
    279 
    280 /*
    281  * _prop_object_externalize_footer --
    282  *	Append the standard XML footer to the externalize buffer.  This
    283  *	also NUL-terminates the buffer.
    284  */
    285 bool
    286 _prop_object_externalize_footer(struct _prop_object_externalize_context *ctx)
    287 {
    288 
    289 	if (_prop_object_externalize_end_tag(ctx, "plist") == false ||
    290 	    _prop_object_externalize_append_char(ctx, '\0') == false)
    291 		return (false);
    292 
    293 	return (true);
    294 }
    295 
    296 /*
    297  * _prop_object_externalize_context_alloc --
    298  *	Allocate an externalize context.
    299  */
    300 struct _prop_object_externalize_context *
    301 _prop_object_externalize_context_alloc(void)
    302 {
    303 	struct _prop_object_externalize_context *ctx;
    304 
    305 	ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP);
    306 	if (ctx != NULL) {
    307 		ctx->poec_buf = _PROP_MALLOC(BUF_EXPAND, M_TEMP);
    308 		if (ctx->poec_buf == NULL) {
    309 			_PROP_FREE(ctx, M_TEMP);
    310 			return (NULL);
    311 		}
    312 		ctx->poec_len = 0;
    313 		ctx->poec_capacity = BUF_EXPAND;
    314 		ctx->poec_depth = 0;
    315 	}
    316 	return (ctx);
    317 }
    318 
    319 /*
    320  * _prop_object_externalize_context_free --
    321  *	Free an externalize context.
    322  */
    323 void
    324 _prop_object_externalize_context_free(
    325 		struct _prop_object_externalize_context *ctx)
    326 {
    327 
    328 	/* Buffer is always freed by the caller. */
    329 	_PROP_FREE(ctx, M_TEMP);
    330 }
    331 
    332 /*
    333  * _prop_object_internalize_skip_comment --
    334  *	Skip the body and end tag of a comment.
    335  */
    336 static bool
    337 _prop_object_internalize_skip_comment(
    338 				struct _prop_object_internalize_context *ctx)
    339 {
    340 	const char *cp = ctx->poic_cp;
    341 
    342 	while (!_PROP_EOF(*cp)) {
    343 		if (cp[0] == '-' &&
    344 		    cp[1] == '-' &&
    345 		    cp[2] == '>') {
    346 			ctx->poic_cp = cp + 3;
    347 			return (true);
    348 		}
    349 		cp++;
    350 	}
    351 
    352 	return (false);		/* ran out of buffer */
    353 }
    354 
    355 /*
    356  * _prop_object_internalize_find_tag --
    357  *	Find the next tag in an XML stream.  Optionally compare the found
    358  *	tag to an expected tag name.  State of the context is undefined
    359  *	if this routine returns false.  Upon success, the context points
    360  *	to the first octet after the tag.
    361  */
    362 bool
    363 _prop_object_internalize_find_tag(struct _prop_object_internalize_context *ctx,
    364 		      const char *tag, _prop_tag_type_t type)
    365 {
    366 	const char *cp;
    367 	size_t taglen;
    368 
    369 	if (tag != NULL)
    370 		taglen = strlen(tag);
    371 	else
    372 		taglen = 0;
    373 
    374  start_over:
    375 	cp = ctx->poic_cp;
    376 
    377 	/*
    378 	 * Find the start of the tag.
    379 	 */
    380 	while (_PROP_ISSPACE(*cp))
    381 		cp++;
    382 	if (_PROP_EOF(*cp))
    383 		return (false);
    384 
    385 	if (*cp != '<')
    386 		return (false);
    387 
    388 	ctx->poic_tag_start = cp++;
    389 	if (_PROP_EOF(*cp))
    390 		return (false);
    391 
    392 	if (*cp == '!') {
    393 		if (cp[1] != '-' || cp[2] != '-')
    394 			return (false);
    395 		/*
    396 		 * Comment block -- only allowed if we are allowed to
    397 		 * return a start tag.
    398 		 */
    399 		if (type == _PROP_TAG_TYPE_END)
    400 			return (false);
    401 		ctx->poic_cp = cp + 3;
    402 		if (_prop_object_internalize_skip_comment(ctx) == false)
    403 			return (false);
    404 		goto start_over;
    405 	}
    406 
    407 	if (*cp == '/') {
    408 		if (type != _PROP_TAG_TYPE_END &&
    409 		    type != _PROP_TAG_TYPE_EITHER)
    410 			return (false);
    411 		cp++;
    412 		if (_PROP_EOF(*cp))
    413 			return (false);
    414 		ctx->poic_tag_type = _PROP_TAG_TYPE_END;
    415 	} else {
    416 		if (type != _PROP_TAG_TYPE_START &&
    417 		    type != _PROP_TAG_TYPE_EITHER)
    418 			return (false);
    419 		ctx->poic_tag_type = _PROP_TAG_TYPE_START;
    420 	}
    421 
    422 	ctx->poic_tagname = cp;
    423 
    424 	while (!_PROP_ISSPACE(*cp) && *cp != '/' && *cp != '>')
    425 		cp++;
    426 	if (_PROP_EOF(*cp))
    427 		return (false);
    428 
    429 	ctx->poic_tagname_len = cp - ctx->poic_tagname;
    430 
    431 	/* Make sure this is the tag we're looking for. */
    432 	if (tag != NULL &&
    433 	    (taglen != ctx->poic_tagname_len ||
    434 	     memcmp(tag, ctx->poic_tagname, taglen) != 0))
    435 		return (false);
    436 
    437 	/* Check for empty tag. */
    438 	if (*cp == '/') {
    439 		if (ctx->poic_tag_type != _PROP_TAG_TYPE_START)
    440 			return(false);		/* only valid on start tags */
    441 		ctx->poic_is_empty_element = true;
    442 		cp++;
    443 		if (_PROP_EOF(*cp) || *cp != '>')
    444 			return (false);
    445 	} else
    446 		ctx->poic_is_empty_element = false;
    447 
    448 	/* Easy case of no arguments. */
    449 	if (*cp == '>') {
    450 		ctx->poic_tagattr = NULL;
    451 		ctx->poic_tagattr_len = 0;
    452 		ctx->poic_tagattrval = NULL;
    453 		ctx->poic_tagattrval_len = 0;
    454 		ctx->poic_cp = cp + 1;
    455 		return (true);
    456 	}
    457 
    458 	_PROP_ASSERT(!_PROP_EOF(*cp));
    459 	cp++;
    460 	if (_PROP_EOF(*cp))
    461 		return (false);
    462 
    463 	while (_PROP_ISSPACE(*cp))
    464 		cp++;
    465 	if (_PROP_EOF(*cp))
    466 		return (false);
    467 
    468 	ctx->poic_tagattr = cp;
    469 
    470 	while (!_PROP_ISSPACE(*cp) && *cp != '=')
    471 		cp++;
    472 	if (_PROP_EOF(*cp))
    473 		return (false);
    474 
    475 	ctx->poic_tagattr_len = cp - ctx->poic_tagattr;
    476 
    477 	cp++;
    478 	if (*cp != '\"')
    479 		return (false);
    480 	cp++;
    481 	if (_PROP_EOF(*cp))
    482 		return (false);
    483 
    484 	ctx->poic_tagattrval = cp;
    485 	while (*cp != '\"')
    486 		cp++;
    487 	if (_PROP_EOF(*cp))
    488 		return (false);
    489 	ctx->poic_tagattrval_len = cp - ctx->poic_tagattrval;
    490 
    491 	cp++;
    492 	if (*cp != '>')
    493 		return (false);
    494 
    495 	ctx->poic_cp = cp + 1;
    496 	return (true);
    497 }
    498 
    499 /*
    500  * _prop_object_internalize_decode_string --
    501  *	Decode an encoded string.
    502  */
    503 bool
    504 _prop_object_internalize_decode_string(
    505 				struct _prop_object_internalize_context *ctx,
    506 				char *target, size_t targsize, size_t *sizep,
    507 				const char **cpp)
    508 {
    509 	const char *src;
    510 	size_t tarindex;
    511 	char c;
    512 
    513 	tarindex = 0;
    514 	src = ctx->poic_cp;
    515 
    516 	for (;;) {
    517 		if (_PROP_EOF(*src))
    518 			return (false);
    519 		if (*src == '<') {
    520 			break;
    521 		}
    522 
    523 		if ((c = *src) == '&') {
    524 			if (src[1] == 'a' &&
    525 			    src[2] == 'm' &&
    526 			    src[3] == 'p' &&
    527 			    src[4] == ';') {
    528 			    	c = '&';
    529 				src += 5;
    530 			} else if (src[1] == 'l' &&
    531 				   src[2] == 't' &&
    532 				   src[3] == ';') {
    533 				c = '<';
    534 				src += 4;
    535 			} else if (src[1] == 'g' &&
    536 				   src[2] == 't' &&
    537 				   src[3] == ';') {
    538 				c = '>';
    539 				src += 4;
    540 			} else if (src[1] == 'a' &&
    541 				   src[2] == 'p' &&
    542 				   src[3] == 'o' &&
    543 				   src[4] == 's' &&
    544 				   src[5] == ';') {
    545 				c = '\'';
    546 				src += 6;
    547 			} else if (src[1] == 'q' &&
    548 				   src[2] == 'u' &&
    549 				   src[3] == 'o' &&
    550 				   src[4] == 't' &&
    551 				   src[5] == ';') {
    552 				c = '\"';
    553 				src += 6;
    554 			} else
    555 				return (false);
    556 		} else
    557 			src++;
    558 		if (target) {
    559 			if (tarindex >= targsize)
    560 				return (false);
    561 			target[tarindex] = c;
    562 		}
    563 		tarindex++;
    564 	}
    565 
    566 	_PROP_ASSERT(*src == '<');
    567 	if (sizep != NULL)
    568 		*sizep = tarindex;
    569 	if (cpp != NULL)
    570 		*cpp = src;
    571 
    572 	return (true);
    573 }
    574 
    575 /*
    576  * _prop_object_internalize_match --
    577  *	Returns true if the two character streams match.
    578  */
    579 bool
    580 _prop_object_internalize_match(const char *str1, size_t len1,
    581 			       const char *str2, size_t len2)
    582 {
    583 
    584 	return (len1 == len2 && memcmp(str1, str2, len1) == 0);
    585 }
    586 
    587 #define	INTERNALIZER(t, f)			\
    588 {	t,	sizeof(t) - 1,		f	}
    589 
    590 static const struct _prop_object_internalizer {
    591 	const char			*poi_tag;
    592 	size_t				poi_taglen;
    593 	prop_object_internalizer_t	poi_intern;
    594 } _prop_object_internalizer_table[] = {
    595 	INTERNALIZER("array", _prop_array_internalize),
    596 
    597 	INTERNALIZER("true", _prop_bool_internalize),
    598 	INTERNALIZER("false", _prop_bool_internalize),
    599 
    600 	INTERNALIZER("data", _prop_data_internalize),
    601 
    602 	INTERNALIZER("dict", _prop_dictionary_internalize),
    603 
    604 	INTERNALIZER("integer", _prop_number_internalize),
    605 
    606 	INTERNALIZER("string", _prop_string_internalize),
    607 
    608 	{ 0, 0, NULL }
    609 };
    610 
    611 #undef INTERNALIZER
    612 
    613 /*
    614  * _prop_object_internalize_by_tag --
    615  *	Determine the object type from the tag in the context and
    616  *	internalize it.
    617  */
    618 prop_object_t
    619 _prop_object_internalize_by_tag(struct _prop_object_internalize_context *ctx)
    620 {
    621 	const struct _prop_object_internalizer *poi;
    622 	prop_object_t obj, parent_obj;
    623 	void *data, *iter;
    624 	prop_object_internalizer_continue_t iter_func;
    625 	struct _prop_stack stack;
    626 
    627 	_prop_stack_init(&stack);
    628 
    629 match_start:
    630 	for (poi = _prop_object_internalizer_table;
    631 	     poi->poi_tag != NULL; poi++) {
    632 		if (_prop_object_internalize_match(ctx->poic_tagname,
    633 						   ctx->poic_tagname_len,
    634 						   poi->poi_tag,
    635 						   poi->poi_taglen))
    636 			break;
    637 	}
    638 	if (poi == NULL) {
    639 		while (_prop_stack_pop(&stack, &obj, &iter, &data, NULL)) {
    640 			iter_func = (prop_object_internalizer_continue_t)iter;
    641 			(*iter_func)(&stack, &obj, ctx, data, NULL);
    642 		}
    643 
    644 		return (NULL);
    645 	}
    646 
    647 	obj = NULL;
    648 	if (!(*poi->poi_intern)(&stack, &obj, ctx))
    649 		goto match_start;
    650 
    651 	parent_obj = obj;
    652 	while (_prop_stack_pop(&stack, &parent_obj, &iter, &data, NULL)) {
    653 		iter_func = (prop_object_internalizer_continue_t)iter;
    654 		if (!(*iter_func)(&stack, &parent_obj, ctx, data, obj))
    655 			goto match_start;
    656 		obj = parent_obj;
    657 	}
    658 
    659 	return (parent_obj);
    660 }
    661 
    662 prop_object_t
    663 _prop_generic_internalize(const char *xml, const char *master_tag)
    664 {
    665 	prop_object_t obj = NULL;
    666 	struct _prop_object_internalize_context *ctx;
    667 
    668 	ctx = _prop_object_internalize_context_alloc(xml);
    669 	if (ctx == NULL)
    670 		return (NULL);
    671 
    672 	/* We start with a <plist> tag. */
    673 	if (_prop_object_internalize_find_tag(ctx, "plist",
    674 					      _PROP_TAG_TYPE_START) == false)
    675 		goto out;
    676 
    677 	/* Plist elements cannot be empty. */
    678 	if (ctx->poic_is_empty_element)
    679 		goto out;
    680 
    681 	/*
    682 	 * We don't understand any plist attributes, but Apple XML
    683 	 * property lists often have a "version" attribute.  If we
    684 	 * see that one, we simply ignore it.
    685 	 */
    686 	if (ctx->poic_tagattr != NULL &&
    687 	    !_PROP_TAGATTR_MATCH(ctx, "version"))
    688 		goto out;
    689 
    690 	/* Next we expect to see opening master_tag. */
    691 	if (_prop_object_internalize_find_tag(ctx, master_tag,
    692 					      _PROP_TAG_TYPE_START) == false)
    693 		goto out;
    694 
    695 	obj = _prop_object_internalize_by_tag(ctx);
    696 	if (obj == NULL)
    697 		goto out;
    698 
    699 	/*
    700 	 * We've advanced past the closing master_tag.
    701 	 * Now we want </plist>.
    702 	 */
    703 	if (_prop_object_internalize_find_tag(ctx, "plist",
    704 					      _PROP_TAG_TYPE_END) == false) {
    705 		prop_object_release(obj);
    706 		obj = NULL;
    707 	}
    708 
    709  out:
    710  	_prop_object_internalize_context_free(ctx);
    711 	return (obj);
    712 }
    713 
    714 /*
    715  * _prop_object_internalize_context_alloc --
    716  *	Allocate an internalize context.
    717  */
    718 struct _prop_object_internalize_context *
    719 _prop_object_internalize_context_alloc(const char *xml)
    720 {
    721 	struct _prop_object_internalize_context *ctx;
    722 
    723 	ctx = _PROP_MALLOC(sizeof(struct _prop_object_internalize_context),
    724 			   M_TEMP);
    725 	if (ctx == NULL)
    726 		return (NULL);
    727 
    728 	ctx->poic_xml = ctx->poic_cp = xml;
    729 
    730 	/*
    731 	 * Skip any whitespace and XML preamble stuff that we don't
    732 	 * know about / care about.
    733 	 */
    734 	for (;;) {
    735 		while (_PROP_ISSPACE(*xml))
    736 			xml++;
    737 		if (_PROP_EOF(*xml) || *xml != '<')
    738 			goto bad;
    739 
    740 #define	MATCH(str)	(memcmp(&xml[1], str, sizeof(str) - 1) == 0)
    741 
    742 		/*
    743 		 * Skip over the XML preamble that Apple XML property
    744 		 * lists usually include at the top of the file.
    745 		 */
    746 		if (MATCH("?xml ") ||
    747 		    MATCH("!DOCTYPE plist")) {
    748 			while (*xml != '>' && !_PROP_EOF(*xml))
    749 				xml++;
    750 			if (_PROP_EOF(*xml))
    751 				goto bad;
    752 			xml++;	/* advance past the '>' */
    753 			continue;
    754 		}
    755 
    756 		if (MATCH("<!--")) {
    757 			ctx->poic_cp = xml + 4;
    758 			if (_prop_object_internalize_skip_comment(ctx) == false)
    759 				goto bad;
    760 			xml = ctx->poic_cp;
    761 			continue;
    762 		}
    763 
    764 #undef MATCH
    765 
    766 		/*
    767 		 * We don't think we should skip it, so let's hope we can
    768 		 * parse it.
    769 		 */
    770 		break;
    771 	}
    772 
    773 	ctx->poic_cp = xml;
    774 	return (ctx);
    775  bad:
    776 	_PROP_FREE(ctx, M_TEMP);
    777 	return (NULL);
    778 }
    779 
    780 /*
    781  * _prop_object_internalize_context_free --
    782  *	Free an internalize context.
    783  */
    784 void
    785 _prop_object_internalize_context_free(
    786 		struct _prop_object_internalize_context *ctx)
    787 {
    788 
    789 	_PROP_FREE(ctx, M_TEMP);
    790 }
    791 
    792 #if !defined(_KERNEL) && !defined(_STANDALONE)
    793 /*
    794  * _prop_object_externalize_file_dirname --
    795  *	dirname(3), basically.  We have to roll our own because the
    796  *	system dirname(3) isn't reentrant.
    797  */
    798 static void
    799 _prop_object_externalize_file_dirname(const char *path, char *result)
    800 {
    801 	const char *lastp;
    802 	size_t len;
    803 
    804 	/*
    805 	 * If `path' is a NULL pointer or points to an empty string,
    806 	 * return ".".
    807 	 */
    808 	if (path == NULL || *path == '\0')
    809 		goto singledot;
    810 
    811 	/* String trailing slashes, if any. */
    812 	lastp = path + strlen(path) - 1;
    813 	while (lastp != path && *lastp == '/')
    814 		lastp--;
    815 
    816 	/* Terminate path at the last occurrence of '/'. */
    817 	do {
    818 		if (*lastp == '/') {
    819 			/* Strip trailing slashes, if any. */
    820 			while (lastp != path && *lastp == '/')
    821 				lastp--;
    822 
    823 			/* ...and copy the result into the result buffer. */
    824 			len = (lastp - path) + 1 /* last char */;
    825 			if (len > (PATH_MAX - 1))
    826 				len = PATH_MAX - 1;
    827 
    828 			memcpy(result, path, len);
    829 			result[len] = '\0';
    830 			return;
    831 		}
    832 	} while (--lastp >= path);
    833 
    834  	/* No /'s found, return ".". */
    835  singledot:
    836 	strcpy(result, ".");
    837 }
    838 
    839 /*
    840  * _prop_object_externalize_write_file --
    841  *	Write an externalized dictionary to the specified file.
    842  *	The file is written atomically from the caller's perspective,
    843  *	and the mode set to 0666 modified by the caller's umask.
    844  */
    845 bool
    846 _prop_object_externalize_write_file(const char *fname, const char *xml,
    847     size_t len)
    848 {
    849 	char tname[PATH_MAX];
    850 	int fd;
    851 	int save_errno;
    852 
    853 	if (len > SSIZE_MAX) {
    854 		errno = EFBIG;
    855 		return (false);
    856 	}
    857 
    858 	/*
    859 	 * Get the directory name where the file is to be written
    860 	 * and create the temporary file.
    861 	 *
    862 	 * We don't use mkstemp() because mkstemp() always creates the
    863 	 * file with mode 0600.  We do, however, use mktemp() safely.
    864 	 */
    865  again:
    866 	_prop_object_externalize_file_dirname(fname, tname);
    867 	if (strlcat(tname, "/.plistXXXXXX", sizeof(tname)) >= sizeof(tname)) {
    868 		errno = ENAMETOOLONG;
    869 		return (false);
    870 	}
    871 	if (mktemp(tname) == NULL)
    872 		return (false);
    873 	if ((fd = open(tname, O_CREAT|O_RDWR|O_EXCL, 0666)) == -1) {
    874 		if (errno == EEXIST)
    875 			goto again;
    876 		return (false);
    877 	}
    878 
    879 	if (write(fd, xml, len) != (ssize_t)len)
    880 		goto bad;
    881 
    882 	if (fsync(fd) == -1)
    883 		goto bad;
    884 
    885 	(void) close(fd);
    886 	fd = -1;
    887 
    888 	if (rename(tname, fname) == -1)
    889 		goto bad;
    890 
    891 	return (true);
    892 
    893  bad:
    894 	save_errno = errno;
    895 	if (fd != -1)
    896 		(void) close(fd);
    897 	(void) unlink(tname);
    898 	errno = save_errno;
    899 	return (false);
    900 }
    901 
    902 /*
    903  * _prop_object_internalize_map_file --
    904  *	Map a file for the purpose of internalizing it.
    905  */
    906 struct _prop_object_internalize_mapped_file *
    907 _prop_object_internalize_map_file(const char *fname)
    908 {
    909 	struct stat sb;
    910 	struct _prop_object_internalize_mapped_file *mf;
    911 	size_t pgsize = (size_t)sysconf(_SC_PAGESIZE);
    912 	size_t pgmask = pgsize - 1;
    913 	bool need_guard = false;
    914 	int fd;
    915 
    916 	mf = _PROP_MALLOC(sizeof(*mf), M_TEMP);
    917 	if (mf == NULL)
    918 		return (NULL);
    919 
    920 	fd = open(fname, O_RDONLY, 0400);
    921 	if (fd == -1) {
    922 		_PROP_FREE(mf, M_TEMP);
    923 		return (NULL);
    924 	}
    925 
    926 	if (fstat(fd, &sb) == -1) {
    927 		(void) close(fd);
    928 		_PROP_FREE(mf, M_TEMP);
    929 		return (NULL);
    930 	}
    931 	mf->poimf_mapsize = ((size_t)sb.st_size + pgmask) & ~pgmask;
    932 	if (mf->poimf_mapsize < sb.st_size) {
    933 		(void) close(fd);
    934 		_PROP_FREE(mf, M_TEMP);
    935 		return (NULL);
    936 	}
    937 
    938 	/*
    939 	 * If the file length is an integral number of pages, then we
    940 	 * need to map a guard page at the end in order to provide the
    941 	 * necessary NUL-termination of the buffer.
    942 	 */
    943 	if ((sb.st_size & pgmask) == 0)
    944 		need_guard = true;
    945 
    946 	mf->poimf_xml = mmap(NULL, need_guard ? mf->poimf_mapsize + pgsize
    947 			    		      : mf->poimf_mapsize,
    948 			    PROT_READ, MAP_FILE|MAP_SHARED, fd, (off_t)0);
    949 	(void) close(fd);
    950 	if (mf->poimf_xml == MAP_FAILED) {
    951 		_PROP_FREE(mf, M_TEMP);
    952 		return (NULL);
    953 	}
    954 	(void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_SEQUENTIAL);
    955 
    956 	if (need_guard) {
    957 		if (mmap(mf->poimf_xml + mf->poimf_mapsize,
    958 			 pgsize, PROT_READ,
    959 			 MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1,
    960 			 (off_t)0) == MAP_FAILED) {
    961 			(void) munmap(mf->poimf_xml, mf->poimf_mapsize);
    962 			_PROP_FREE(mf, M_TEMP);
    963 			return (NULL);
    964 		}
    965 		mf->poimf_mapsize += pgsize;
    966 	}
    967 
    968 	return (mf);
    969 }
    970 
    971 /*
    972  * _prop_object_internalize_unmap_file --
    973  *	Unmap a file previously mapped for internalizing.
    974  */
    975 void
    976 _prop_object_internalize_unmap_file(
    977     struct _prop_object_internalize_mapped_file *mf)
    978 {
    979 
    980 	(void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_DONTNEED);
    981 	(void) munmap(mf->poimf_xml, mf->poimf_mapsize);
    982 	_PROP_FREE(mf, M_TEMP);
    983 }
    984 #endif /* !_KERNEL && !_STANDALONE */
    985 
    986 /*
    987  * Retain / release serialization --
    988  *
    989  * Eventually we would like to use atomic operations.  But until we have
    990  * an MI API for them that is common to userland and the kernel, we will
    991  * use a lock instead.
    992  *
    993  * We use a single global mutex for all serialization.  In the kernel, because
    994  * we are still under a biglock, this will basically never contend (properties
    995  * cannot be manipulated at interrupt level).  In userland, this will cost
    996  * nothing for single-threaded programs.  For multi-threaded programs, there
    997  * could be contention, but it probably won't cost that much unless the program
    998  * makes heavy use of property lists.
    999  */
   1000 _PROP_MUTEX_DECL_STATIC(_prop_refcnt_mutex)
   1001 #define	_PROP_REFCNT_LOCK()	_PROP_MUTEX_LOCK(_prop_refcnt_mutex)
   1002 #define	_PROP_REFCNT_UNLOCK()	_PROP_MUTEX_UNLOCK(_prop_refcnt_mutex)
   1003 
   1004 /*
   1005  * prop_object_retain --
   1006  *	Increment the reference count on an object.
   1007  */
   1008 void
   1009 prop_object_retain(prop_object_t obj)
   1010 {
   1011 	struct _prop_object *po = obj;
   1012 	uint32_t ocnt;
   1013 
   1014 	_PROP_REFCNT_LOCK();
   1015 	ocnt = po->po_refcnt++;
   1016 	_PROP_REFCNT_UNLOCK();
   1017 
   1018 	_PROP_ASSERT(ocnt != 0xffffffffU);
   1019 }
   1020 
   1021 /*
   1022  * prop_object_release_emergency
   1023  *	A direct free with prop_object_release failed.
   1024  *	Walk down the tree until a leaf is found and
   1025  *	free that. Do not recurse to avoid stack overflows.
   1026  *
   1027  *	This is a slow edge condition, but necessary to
   1028  *	guarantee that an object can always be freed.
   1029  */
   1030 static void
   1031 prop_object_release_emergency(prop_object_t obj)
   1032 {
   1033 	struct _prop_object *po;
   1034 	prop_object_t parent = NULL;
   1035 	uint32_t ocnt;
   1036 
   1037 	for (;;) {
   1038 		po = obj;
   1039 		_PROP_ASSERT(obj);
   1040 
   1041 		_PROP_REFCNT_LOCK();
   1042     		ocnt = po->po_refcnt--;
   1043 		_PROP_REFCNT_UNLOCK();
   1044 
   1045 		_PROP_ASSERT(ocnt != 0);
   1046 		if (ocnt != 1)
   1047 			break;
   1048 
   1049 		_PROP_ASSERT(po->po_type);
   1050 		if ((po->po_type->pot_free)(NULL, &obj) == 0)
   1051 			break;
   1052 
   1053 		parent = po;
   1054 		_PROP_REFCNT_LOCK();
   1055 		++po->po_refcnt;
   1056 		_PROP_REFCNT_UNLOCK();
   1057 	}
   1058 	_PROP_ASSERT(parent);
   1059 	/* One object was just freed. */
   1060 	po = parent;
   1061 	(*po->po_type->pot_emergency_free)(parent);
   1062 }
   1063 
   1064 /*
   1065  * prop_object_release --
   1066  *	Decrement the reference count on an object.
   1067  *
   1068  *	Free the object if we are releasing the final
   1069  *	reference.
   1070  */
   1071 void
   1072 prop_object_release(prop_object_t obj)
   1073 {
   1074 	struct _prop_object *po;
   1075 	struct _prop_stack stack;
   1076 	int ret;
   1077 	uint32_t ocnt;
   1078 
   1079 	_prop_stack_init(&stack);
   1080 
   1081 	do {
   1082 		do {
   1083 			po = obj;
   1084 			_PROP_ASSERT(obj);
   1085 
   1086 			_PROP_REFCNT_LOCK();
   1087 			ocnt = po->po_refcnt--;
   1088 			_PROP_REFCNT_UNLOCK();
   1089 
   1090 			_PROP_ASSERT(ocnt != 0);
   1091 			if (ocnt != 1) {
   1092 				ret = 0;
   1093 				break;
   1094 			}
   1095 
   1096 			ret = (po->po_type->pot_free)(&stack, &obj);
   1097 
   1098 			if (ret == _PROP_OBJECT_FREE_DONE)
   1099 				break;
   1100 
   1101 			_PROP_REFCNT_LOCK();
   1102 			++po->po_refcnt;
   1103 			_PROP_REFCNT_UNLOCK();
   1104 		} while (ret == _PROP_OBJECT_FREE_RECURSE);
   1105 		if (ret == _PROP_OBJECT_FREE_FAILED)
   1106 			prop_object_release_emergency(obj);
   1107 	} while (_prop_stack_pop(&stack, &obj, NULL, NULL, NULL));
   1108 }
   1109 
   1110 /*
   1111  * prop_object_type --
   1112  *	Return the type of an object.
   1113  */
   1114 prop_type_t
   1115 prop_object_type(prop_object_t obj)
   1116 {
   1117 	struct _prop_object *po = obj;
   1118 
   1119 	if (obj == NULL)
   1120 		return (PROP_TYPE_UNKNOWN);
   1121 
   1122 	return (po->po_type->pot_type);
   1123 }
   1124 
   1125 /*
   1126  * prop_object_equals --
   1127  *	Returns true if thw two objects are equivalent.
   1128  */
   1129 bool
   1130 prop_object_equals(prop_object_t obj1, prop_object_t obj2)
   1131 {
   1132 	return (prop_object_equals_with_error(obj1, obj2, NULL));
   1133 }
   1134 
   1135 bool
   1136 prop_object_equals_with_error(prop_object_t obj1, prop_object_t obj2,
   1137     bool *error_flag)
   1138 {
   1139 	struct _prop_object *po1;
   1140 	struct _prop_object *po2;
   1141 	void *stored_pointer1, *stored_pointer2;
   1142 	prop_object_t next_obj1, next_obj2;
   1143 	struct _prop_stack stack;
   1144 	int ret;
   1145 
   1146 	_prop_stack_init(&stack);
   1147 	if (error_flag)
   1148 		*error_flag = false;
   1149 
   1150  start_subtree:
   1151 	stored_pointer1 = NULL;
   1152 	stored_pointer2 = NULL;
   1153 	po1 = obj1;
   1154 	po2 = obj2;
   1155 
   1156 	if (po1->po_type != po2->po_type)
   1157 		return (false);
   1158 
   1159  continue_subtree:
   1160 	ret = (*po1->po_type->pot_equals)(obj1, obj2, &stored_pointer1, &stored_pointer2,
   1161 	    &next_obj1, &next_obj2);
   1162 	if (ret == _PROP_OBJECT_EQUALS_FALSE)
   1163 		goto finish;
   1164 	if (ret == _PROP_OBJECT_EQUALS_TRUE) {
   1165 		if (!_prop_stack_pop(&stack, &obj1, &obj2,
   1166 				     &stored_pointer1, &stored_pointer2))
   1167 			return true;
   1168 		goto continue_subtree;
   1169 	}
   1170 	_PROP_ASSERT(ret == _PROP_OBJECT_EQUALS_RECURSE);
   1171 
   1172 	if (!_prop_stack_push(&stack, obj1, obj2,
   1173 			      stored_pointer1, stored_pointer2)) {
   1174 		if (error_flag)
   1175 			*error_flag = true;
   1176 		goto finish;
   1177 	}
   1178 	obj1 = next_obj1;
   1179 	obj2 = next_obj2;
   1180 	goto start_subtree;
   1181 
   1182 finish:
   1183 	while (_prop_stack_pop(&stack, &obj1, &obj2, NULL, NULL)) {
   1184 		po1 = obj1;
   1185 		(*po1->po_type->pot_equals_finish)(obj1, obj2);
   1186 	}
   1187 	return (false);
   1188 }
   1189 
   1190 /*
   1191  * prop_object_iterator_next --
   1192  *	Return the next item during an iteration.
   1193  */
   1194 prop_object_t
   1195 prop_object_iterator_next(prop_object_iterator_t pi)
   1196 {
   1197 
   1198 	return ((*pi->pi_next_object)(pi));
   1199 }
   1200 
   1201 /*
   1202  * prop_object_iterator_reset --
   1203  *	Reset the iterator to the first object so as to restart
   1204  *	iteration.
   1205  */
   1206 void
   1207 prop_object_iterator_reset(prop_object_iterator_t pi)
   1208 {
   1209 
   1210 	(*pi->pi_reset)(pi);
   1211 }
   1212 
   1213 /*
   1214  * prop_object_iterator_release --
   1215  *	Release the object iterator.
   1216  */
   1217 void
   1218 prop_object_iterator_release(prop_object_iterator_t pi)
   1219 {
   1220 
   1221 	prop_object_release(pi->pi_obj);
   1222 	_PROP_FREE(pi, M_TEMP);
   1223 }
   1224