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