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