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 "<") == false)
203 return (false);
204 break;
205 case '>':
206 if (_prop_object_externalize_append_cstring(ctx,
207 ">") == false)
208 return (false);
209 break;
210 case '&':
211 if (_prop_object_externalize_append_cstring(ctx,
212 "&") == 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