vm: fix potential null deref
[minix.git] / common / lib / libprop / prop_object.c
blobfd0f14de9400482fce9eccf856f07226a965f941
1 /* $NetBSD: prop_object.c,v 1.27 2011/04/20 20:00:07 martin Exp $ */
3 /*-
4 * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe.
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.
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.
32 #include <prop/prop_object.h>
33 #include "prop_object_impl.h"
35 #if !defined(_KERNEL) && !defined(_STANDALONE)
36 #include <sys/mman.h>
37 #include <sys/stat.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #include <unistd.h>
42 #include <assert.h>
43 #endif
44 #include <sys/atomic.h>
46 #ifdef _STANDALONE
47 void *
48 _prop_standalone_calloc(size_t size)
50 void *rv;
52 rv = alloc(size);
53 if (rv != NULL)
54 memset(rv, 0, size);
56 return (rv);
59 void *
60 _prop_standalone_realloc(void *v, size_t size)
62 void *rv;
64 rv = alloc(size);
65 if (rv != NULL) {
66 memcpy(rv, v, size); /* XXX */
67 dealloc(v, 0); /* XXX */
70 return (rv);
72 #endif /* _STANDALONE */
75 * _prop_object_init --
76 * Initialize an object. Called when sub-classes create
77 * an instance.
79 void
80 _prop_object_init(struct _prop_object *po, const struct _prop_object_type *pot)
83 po->po_type = pot;
84 po->po_refcnt = 1;
88 * _prop_object_fini --
89 * Finalize an object. Called when sub-classes destroy
90 * an instance.
92 /*ARGSUSED*/
93 void
94 _prop_object_fini(struct _prop_object *po _PROP_ARG_UNUSED)
96 /* Nothing to do, currently. */
100 * _prop_object_externalize_start_tag --
101 * Append an XML-style start tag to the externalize buffer.
103 bool
104 _prop_object_externalize_start_tag(
105 struct _prop_object_externalize_context *ctx, const char *tag)
107 unsigned int i;
109 for (i = 0; i < ctx->poec_depth; i++) {
110 if (_prop_object_externalize_append_char(ctx, '\t') == false)
111 return (false);
113 if (_prop_object_externalize_append_char(ctx, '<') == false ||
114 _prop_object_externalize_append_cstring(ctx, tag) == false ||
115 _prop_object_externalize_append_char(ctx, '>') == false)
116 return (false);
118 return (true);
122 * _prop_object_externalize_end_tag --
123 * Append an XML-style end tag to the externalize buffer.
125 bool
126 _prop_object_externalize_end_tag(
127 struct _prop_object_externalize_context *ctx, const char *tag)
130 if (_prop_object_externalize_append_char(ctx, '<') == false ||
131 _prop_object_externalize_append_char(ctx, '/') == false ||
132 _prop_object_externalize_append_cstring(ctx, tag) == false ||
133 _prop_object_externalize_append_char(ctx, '>') == false ||
134 _prop_object_externalize_append_char(ctx, '\n') == false)
135 return (false);
137 return (true);
141 * _prop_object_externalize_empty_tag --
142 * Append an XML-style empty tag to the externalize buffer.
144 bool
145 _prop_object_externalize_empty_tag(
146 struct _prop_object_externalize_context *ctx, const char *tag)
148 unsigned int i;
150 for (i = 0; i < ctx->poec_depth; i++) {
151 if (_prop_object_externalize_append_char(ctx, '\t') == false)
152 return (false);
155 if (_prop_object_externalize_append_char(ctx, '<') == false ||
156 _prop_object_externalize_append_cstring(ctx, tag) == false ||
157 _prop_object_externalize_append_char(ctx, '/') == false ||
158 _prop_object_externalize_append_char(ctx, '>') == false ||
159 _prop_object_externalize_append_char(ctx, '\n') == false)
160 return (false);
162 return (true);
166 * _prop_object_externalize_append_cstring --
167 * Append a C string to the externalize buffer.
169 bool
170 _prop_object_externalize_append_cstring(
171 struct _prop_object_externalize_context *ctx, const char *cp)
174 while (*cp != '\0') {
175 if (_prop_object_externalize_append_char(ctx,
176 (unsigned char) *cp) == false)
177 return (false);
178 cp++;
181 return (true);
185 * _prop_object_externalize_append_encoded_cstring --
186 * Append an encoded C string to the externalize buffer.
188 bool
189 _prop_object_externalize_append_encoded_cstring(
190 struct _prop_object_externalize_context *ctx, const char *cp)
193 while (*cp != '\0') {
194 switch (*cp) {
195 case '<':
196 if (_prop_object_externalize_append_cstring(ctx,
197 "&lt;") == false)
198 return (false);
199 break;
200 case '>':
201 if (_prop_object_externalize_append_cstring(ctx,
202 "&gt;") == false)
203 return (false);
204 break;
205 case '&':
206 if (_prop_object_externalize_append_cstring(ctx,
207 "&amp;") == false)
208 return (false);
209 break;
210 default:
211 if (_prop_object_externalize_append_char(ctx,
212 (unsigned char) *cp) == false)
213 return (false);
214 break;
216 cp++;
219 return (true);
222 #define BUF_EXPAND 256
225 * _prop_object_externalize_append_char --
226 * Append a single character to the externalize buffer.
228 bool
229 _prop_object_externalize_append_char(
230 struct _prop_object_externalize_context *ctx, unsigned char c)
233 _PROP_ASSERT(ctx->poec_capacity != 0);
234 _PROP_ASSERT(ctx->poec_buf != NULL);
235 _PROP_ASSERT(ctx->poec_len <= ctx->poec_capacity);
237 if (ctx->poec_len == ctx->poec_capacity) {
238 char *cp = _PROP_REALLOC(ctx->poec_buf,
239 ctx->poec_capacity + BUF_EXPAND,
240 M_TEMP);
241 if (cp == NULL)
242 return (false);
243 ctx->poec_capacity = ctx->poec_capacity + BUF_EXPAND;
244 ctx->poec_buf = cp;
247 ctx->poec_buf[ctx->poec_len++] = c;
249 return (true);
253 * _prop_object_externalize_header --
254 * Append the standard XML header to the externalize buffer.
256 bool
257 _prop_object_externalize_header(struct _prop_object_externalize_context *ctx)
259 static const char _plist_xml_header[] =
260 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
261 "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n";
263 if (_prop_object_externalize_append_cstring(ctx,
264 _plist_xml_header) == false ||
265 _prop_object_externalize_start_tag(ctx,
266 "plist version=\"1.0\"") == false ||
267 _prop_object_externalize_append_char(ctx, '\n') == false)
268 return (false);
270 return (true);
274 * _prop_object_externalize_footer --
275 * Append the standard XML footer to the externalize buffer. This
276 * also NUL-terminates the buffer.
278 bool
279 _prop_object_externalize_footer(struct _prop_object_externalize_context *ctx)
282 if (_prop_object_externalize_end_tag(ctx, "plist") == false ||
283 _prop_object_externalize_append_char(ctx, '\0') == false)
284 return (false);
286 return (true);
290 * _prop_object_externalize_context_alloc --
291 * Allocate an externalize context.
293 struct _prop_object_externalize_context *
294 _prop_object_externalize_context_alloc(void)
296 struct _prop_object_externalize_context *ctx;
298 ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP);
299 if (ctx != NULL) {
300 ctx->poec_buf = _PROP_MALLOC(BUF_EXPAND, M_TEMP);
301 if (ctx->poec_buf == NULL) {
302 _PROP_FREE(ctx, M_TEMP);
303 return (NULL);
305 ctx->poec_len = 0;
306 ctx->poec_capacity = BUF_EXPAND;
307 ctx->poec_depth = 0;
309 return (ctx);
313 * _prop_object_externalize_context_free --
314 * Free an externalize context.
316 void
317 _prop_object_externalize_context_free(
318 struct _prop_object_externalize_context *ctx)
321 /* Buffer is always freed by the caller. */
322 _PROP_FREE(ctx, M_TEMP);
326 * _prop_object_internalize_skip_comment --
327 * Skip the body and end tag of a comment.
329 static bool
330 _prop_object_internalize_skip_comment(
331 struct _prop_object_internalize_context *ctx)
333 const char *cp = ctx->poic_cp;
335 while (!_PROP_EOF(*cp)) {
336 if (cp[0] == '-' &&
337 cp[1] == '-' &&
338 cp[2] == '>') {
339 ctx->poic_cp = cp + 3;
340 return (true);
342 cp++;
345 return (false); /* ran out of buffer */
349 * _prop_object_internalize_find_tag --
350 * Find the next tag in an XML stream. Optionally compare the found
351 * tag to an expected tag name. State of the context is undefined
352 * if this routine returns false. Upon success, the context points
353 * to the first octet after the tag.
355 bool
356 _prop_object_internalize_find_tag(struct _prop_object_internalize_context *ctx,
357 const char *tag, _prop_tag_type_t type)
359 const char *cp;
360 size_t taglen;
362 if (tag != NULL)
363 taglen = strlen(tag);
364 else
365 taglen = 0;
367 start_over:
368 cp = ctx->poic_cp;
371 * Find the start of the tag.
373 while (_PROP_ISSPACE(*cp))
374 cp++;
375 if (_PROP_EOF(*cp))
376 return (false);
378 if (*cp != '<')
379 return (false);
381 ctx->poic_tag_start = cp++;
382 if (_PROP_EOF(*cp))
383 return (false);
385 if (*cp == '!') {
386 if (cp[1] != '-' || cp[2] != '-')
387 return (false);
389 * Comment block -- only allowed if we are allowed to
390 * return a start tag.
392 if (type == _PROP_TAG_TYPE_END)
393 return (false);
394 ctx->poic_cp = cp + 3;
395 if (_prop_object_internalize_skip_comment(ctx) == false)
396 return (false);
397 goto start_over;
400 if (*cp == '/') {
401 if (type != _PROP_TAG_TYPE_END &&
402 type != _PROP_TAG_TYPE_EITHER)
403 return (false);
404 cp++;
405 if (_PROP_EOF(*cp))
406 return (false);
407 ctx->poic_tag_type = _PROP_TAG_TYPE_END;
408 } else {
409 if (type != _PROP_TAG_TYPE_START &&
410 type != _PROP_TAG_TYPE_EITHER)
411 return (false);
412 ctx->poic_tag_type = _PROP_TAG_TYPE_START;
415 ctx->poic_tagname = cp;
417 while (!_PROP_ISSPACE(*cp) && *cp != '/' && *cp != '>')
418 cp++;
419 if (_PROP_EOF(*cp))
420 return (false);
422 ctx->poic_tagname_len = cp - ctx->poic_tagname;
424 /* Make sure this is the tag we're looking for. */
425 if (tag != NULL &&
426 (taglen != ctx->poic_tagname_len ||
427 memcmp(tag, ctx->poic_tagname, taglen) != 0))
428 return (false);
430 /* Check for empty tag. */
431 if (*cp == '/') {
432 if (ctx->poic_tag_type != _PROP_TAG_TYPE_START)
433 return(false); /* only valid on start tags */
434 ctx->poic_is_empty_element = true;
435 cp++;
436 if (_PROP_EOF(*cp) || *cp != '>')
437 return (false);
438 } else
439 ctx->poic_is_empty_element = false;
441 /* Easy case of no arguments. */
442 if (*cp == '>') {
443 ctx->poic_tagattr = NULL;
444 ctx->poic_tagattr_len = 0;
445 ctx->poic_tagattrval = NULL;
446 ctx->poic_tagattrval_len = 0;
447 ctx->poic_cp = cp + 1;
448 return (true);
451 _PROP_ASSERT(!_PROP_EOF(*cp));
452 cp++;
453 if (_PROP_EOF(*cp))
454 return (false);
456 while (_PROP_ISSPACE(*cp))
457 cp++;
458 if (_PROP_EOF(*cp))
459 return (false);
461 ctx->poic_tagattr = cp;
463 while (!_PROP_ISSPACE(*cp) && *cp != '=')
464 cp++;
465 if (_PROP_EOF(*cp))
466 return (false);
468 ctx->poic_tagattr_len = cp - ctx->poic_tagattr;
470 cp++;
471 if (*cp != '\"')
472 return (false);
473 cp++;
474 if (_PROP_EOF(*cp))
475 return (false);
477 ctx->poic_tagattrval = cp;
478 while (*cp != '\"')
479 cp++;
480 if (_PROP_EOF(*cp))
481 return (false);
482 ctx->poic_tagattrval_len = cp - ctx->poic_tagattrval;
484 cp++;
485 if (*cp != '>')
486 return (false);
488 ctx->poic_cp = cp + 1;
489 return (true);
493 * _prop_object_internalize_decode_string --
494 * Decode an encoded string.
496 bool
497 _prop_object_internalize_decode_string(
498 struct _prop_object_internalize_context *ctx,
499 char *target, size_t targsize, size_t *sizep,
500 const char **cpp)
502 const char *src;
503 size_t tarindex;
504 char c;
506 tarindex = 0;
507 src = ctx->poic_cp;
509 for (;;) {
510 if (_PROP_EOF(*src))
511 return (false);
512 if (*src == '<') {
513 break;
516 if ((c = *src) == '&') {
517 if (src[1] == 'a' &&
518 src[2] == 'm' &&
519 src[3] == 'p' &&
520 src[4] == ';') {
521 c = '&';
522 src += 5;
523 } else if (src[1] == 'l' &&
524 src[2] == 't' &&
525 src[3] == ';') {
526 c = '<';
527 src += 4;
528 } else if (src[1] == 'g' &&
529 src[2] == 't' &&
530 src[3] == ';') {
531 c = '>';
532 src += 4;
533 } else if (src[1] == 'a' &&
534 src[2] == 'p' &&
535 src[3] == 'o' &&
536 src[4] == 's' &&
537 src[5] == ';') {
538 c = '\'';
539 src += 6;
540 } else if (src[1] == 'q' &&
541 src[2] == 'u' &&
542 src[3] == 'o' &&
543 src[4] == 't' &&
544 src[5] == ';') {
545 c = '\"';
546 src += 6;
547 } else
548 return (false);
549 } else
550 src++;
551 if (target) {
552 if (tarindex >= targsize)
553 return (false);
554 target[tarindex] = c;
556 tarindex++;
559 _PROP_ASSERT(*src == '<');
560 if (sizep != NULL)
561 *sizep = tarindex;
562 if (cpp != NULL)
563 *cpp = src;
565 return (true);
569 * _prop_object_internalize_match --
570 * Returns true if the two character streams match.
572 bool
573 _prop_object_internalize_match(const char *str1, size_t len1,
574 const char *str2, size_t len2)
577 return (len1 == len2 && memcmp(str1, str2, len1) == 0);
580 #define INTERNALIZER(t, f) \
581 { t, sizeof(t) - 1, f }
583 static const struct _prop_object_internalizer {
584 const char *poi_tag;
585 size_t poi_taglen;
586 prop_object_internalizer_t poi_intern;
587 } _prop_object_internalizer_table[] = {
588 INTERNALIZER("array", _prop_array_internalize),
590 INTERNALIZER("true", _prop_bool_internalize),
591 INTERNALIZER("false", _prop_bool_internalize),
593 INTERNALIZER("data", _prop_data_internalize),
595 INTERNALIZER("dict", _prop_dictionary_internalize),
597 INTERNALIZER("integer", _prop_number_internalize),
599 INTERNALIZER("string", _prop_string_internalize),
601 { 0, 0, NULL }
604 #undef INTERNALIZER
607 * _prop_object_internalize_by_tag --
608 * Determine the object type from the tag in the context and
609 * internalize it.
611 prop_object_t
612 _prop_object_internalize_by_tag(struct _prop_object_internalize_context *ctx)
614 const struct _prop_object_internalizer *poi;
615 prop_object_t obj, parent_obj;
616 void *data, *iter;
617 prop_object_internalizer_continue_t iter_func;
618 struct _prop_stack stack;
620 _prop_stack_init(&stack);
622 match_start:
623 for (poi = _prop_object_internalizer_table;
624 poi->poi_tag != NULL; poi++) {
625 if (_prop_object_internalize_match(ctx->poic_tagname,
626 ctx->poic_tagname_len,
627 poi->poi_tag,
628 poi->poi_taglen))
629 break;
631 if ((poi == NULL) || (poi->poi_tag == NULL)) {
632 while (_prop_stack_pop(&stack, &obj, &iter, &data, NULL)) {
633 iter_func = (prop_object_internalizer_continue_t)iter;
634 (*iter_func)(&stack, &obj, ctx, data, NULL);
637 return (NULL);
640 obj = NULL;
641 if (!(*poi->poi_intern)(&stack, &obj, ctx))
642 goto match_start;
644 parent_obj = obj;
645 while (_prop_stack_pop(&stack, &parent_obj, &iter, &data, NULL)) {
646 iter_func = (prop_object_internalizer_continue_t)iter;
647 if (!(*iter_func)(&stack, &parent_obj, ctx, data, obj))
648 goto match_start;
649 obj = parent_obj;
652 return (parent_obj);
655 prop_object_t
656 _prop_generic_internalize(const char *xml, const char *master_tag)
658 prop_object_t obj = NULL;
659 struct _prop_object_internalize_context *ctx;
661 ctx = _prop_object_internalize_context_alloc(xml);
662 if (ctx == NULL)
663 return (NULL);
665 /* We start with a <plist> tag. */
666 if (_prop_object_internalize_find_tag(ctx, "plist",
667 _PROP_TAG_TYPE_START) == false)
668 goto out;
670 /* Plist elements cannot be empty. */
671 if (ctx->poic_is_empty_element)
672 goto out;
675 * We don't understand any plist attributes, but Apple XML
676 * property lists often have a "version" attribute. If we
677 * see that one, we simply ignore it.
679 if (ctx->poic_tagattr != NULL &&
680 !_PROP_TAGATTR_MATCH(ctx, "version"))
681 goto out;
683 /* Next we expect to see opening master_tag. */
684 if (_prop_object_internalize_find_tag(ctx, master_tag,
685 _PROP_TAG_TYPE_START) == false)
686 goto out;
688 obj = _prop_object_internalize_by_tag(ctx);
689 if (obj == NULL)
690 goto out;
693 * We've advanced past the closing master_tag.
694 * Now we want </plist>.
696 if (_prop_object_internalize_find_tag(ctx, "plist",
697 _PROP_TAG_TYPE_END) == false) {
698 prop_object_release(obj);
699 obj = NULL;
702 out:
703 _prop_object_internalize_context_free(ctx);
704 return (obj);
708 * _prop_object_internalize_context_alloc --
709 * Allocate an internalize context.
711 struct _prop_object_internalize_context *
712 _prop_object_internalize_context_alloc(const char *xml)
714 struct _prop_object_internalize_context *ctx;
716 ctx = _PROP_MALLOC(sizeof(struct _prop_object_internalize_context),
717 M_TEMP);
718 if (ctx == NULL)
719 return (NULL);
721 ctx->poic_xml = ctx->poic_cp = xml;
724 * Skip any whitespace and XML preamble stuff that we don't
725 * know about / care about.
727 for (;;) {
728 while (_PROP_ISSPACE(*xml))
729 xml++;
730 if (_PROP_EOF(*xml) || *xml != '<')
731 goto bad;
733 #define MATCH(str) (memcmp(&xml[1], str, sizeof(str) - 1) == 0)
736 * Skip over the XML preamble that Apple XML property
737 * lists usually include at the top of the file.
739 if (MATCH("?xml ") ||
740 MATCH("!DOCTYPE plist")) {
741 while (*xml != '>' && !_PROP_EOF(*xml))
742 xml++;
743 if (_PROP_EOF(*xml))
744 goto bad;
745 xml++; /* advance past the '>' */
746 continue;
749 if (MATCH("<!--")) {
750 ctx->poic_cp = xml + 4;
751 if (_prop_object_internalize_skip_comment(ctx) == false)
752 goto bad;
753 xml = ctx->poic_cp;
754 continue;
757 #undef MATCH
760 * We don't think we should skip it, so let's hope we can
761 * parse it.
763 break;
766 ctx->poic_cp = xml;
767 return (ctx);
768 bad:
769 _PROP_FREE(ctx, M_TEMP);
770 return (NULL);
774 * _prop_object_internalize_context_free --
775 * Free an internalize context.
777 void
778 _prop_object_internalize_context_free(
779 struct _prop_object_internalize_context *ctx)
782 _PROP_FREE(ctx, M_TEMP);
785 #if !defined(_KERNEL) && !defined(_STANDALONE)
787 * _prop_object_externalize_file_dirname --
788 * dirname(3), basically. We have to roll our own because the
789 * system dirname(3) isn't reentrant.
791 static void
792 _prop_object_externalize_file_dirname(const char *path, char *result)
794 const char *lastp;
795 size_t len;
798 * If `path' is a NULL pointer or points to an empty string,
799 * return ".".
801 if (path == NULL || *path == '\0')
802 goto singledot;
804 /* String trailing slashes, if any. */
805 lastp = path + strlen(path) - 1;
806 while (lastp != path && *lastp == '/')
807 lastp--;
809 /* Terminate path at the last occurrence of '/'. */
810 do {
811 if (*lastp == '/') {
812 /* Strip trailing slashes, if any. */
813 while (lastp != path && *lastp == '/')
814 lastp--;
816 /* ...and copy the result into the result buffer. */
817 len = (lastp - path) + 1 /* last char */;
818 if (len > (PATH_MAX - 1))
819 len = PATH_MAX - 1;
821 memcpy(result, path, len);
822 result[len] = '\0';
823 return;
825 } while (--lastp >= path);
827 /* No /'s found, return ".". */
828 singledot:
829 strcpy(result, ".");
833 * _prop_object_externalize_write_file --
834 * Write an externalized dictionary to the specified file.
835 * The file is written atomically from the caller's perspective,
836 * and the mode set to 0666 modified by the caller's umask.
838 bool
839 _prop_object_externalize_write_file(const char *fname, const char *xml,
840 size_t len)
842 char tname[PATH_MAX];
843 int fd;
844 int save_errno;
845 mode_t myumask;
847 if (len > SSIZE_MAX) {
848 errno = EFBIG;
849 return (false);
853 * Get the directory name where the file is to be written
854 * and create the temporary file.
856 _prop_object_externalize_file_dirname(fname, tname);
857 if (strlcat(tname, "/.plistXXXXXX", sizeof(tname)) >= sizeof(tname)) {
858 errno = ENAMETOOLONG;
859 return (false);
861 if ((fd = mkstemp(tname)) == -1)
862 return (false);
864 if (write(fd, xml, len) != (ssize_t)len)
865 goto bad;
867 if (fsync(fd) == -1)
868 goto bad;
870 myumask = umask(0);
871 (void)umask(myumask);
872 if (fchmod(fd, 0666 & ~myumask) == -1)
873 goto bad;
875 (void) close(fd);
876 fd = -1;
878 if (rename(tname, fname) == -1)
879 goto bad;
881 return (true);
883 bad:
884 save_errno = errno;
885 if (fd != -1)
886 (void) close(fd);
887 (void) unlink(tname);
888 errno = save_errno;
889 return (false);
893 * _prop_object_internalize_map_file --
894 * Map a file for the purpose of internalizing it.
896 struct _prop_object_internalize_mapped_file *
897 _prop_object_internalize_map_file(const char *fname)
899 struct stat sb;
900 struct _prop_object_internalize_mapped_file *mf;
901 size_t pgsize = (size_t)sysconf(_SC_PAGESIZE);
902 size_t pgmask = pgsize - 1;
903 bool need_guard = false;
904 int fd;
906 mf = _PROP_MALLOC(sizeof(*mf), M_TEMP);
907 if (mf == NULL)
908 return (NULL);
910 fd = open(fname, O_RDONLY, 0400);
911 if (fd == -1) {
912 _PROP_FREE(mf, M_TEMP);
913 return (NULL);
916 if (fstat(fd, &sb) == -1) {
917 (void) close(fd);
918 _PROP_FREE(mf, M_TEMP);
919 return (NULL);
921 mf->poimf_mapsize = ((size_t)sb.st_size + pgmask) & ~pgmask;
922 if (mf->poimf_mapsize < (size_t)sb.st_size) {
923 (void) close(fd);
924 _PROP_FREE(mf, M_TEMP);
925 return (NULL);
929 * If the file length is an integral number of pages, then we
930 * need to map a guard page at the end in order to provide the
931 * necessary NUL-termination of the buffer.
933 if ((sb.st_size & pgmask) == 0)
934 need_guard = true;
936 #ifndef __minix
937 mf->poimf_xml = mmap(NULL, need_guard ? mf->poimf_mapsize + pgsize
938 : mf->poimf_mapsize,
939 PROT_READ, MAP_FILE|MAP_SHARED, fd, (off_t)0);
940 #else
941 mf->poimf_xml = MAP_FAILED;
942 #endif
943 (void) close(fd);
944 if (mf->poimf_xml == MAP_FAILED) {
945 _PROP_FREE(mf, M_TEMP);
946 return (NULL);
949 #ifndef __minix
950 (void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_SEQUENTIAL);
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);
961 mf->poimf_mapsize += pgsize;
963 #endif
965 return (mf);
969 * _prop_object_internalize_unmap_file --
970 * Unmap a file previously mapped for internalizing.
972 void
973 _prop_object_internalize_unmap_file(
974 struct _prop_object_internalize_mapped_file *mf)
977 #ifndef __minix
978 (void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_DONTNEED);
979 (void) munmap(mf->poimf_xml, mf->poimf_mapsize);
980 _PROP_FREE(mf, M_TEMP);
981 #else
982 assert(0);
983 #endif
985 #endif /* !_KERNEL && !_STANDALONE */
988 * prop_object_retain --
989 * Increment the reference count on an object.
991 void
992 prop_object_retain(prop_object_t obj)
994 struct _prop_object *po = obj;
995 uint32_t ncnt;
997 ncnt = atomic_inc_32_nv(&po->po_refcnt);
998 _PROP_ASSERT(ncnt != 0);
1002 * prop_object_release_emergency
1003 * A direct free with prop_object_release failed.
1004 * Walk down the tree until a leaf is found and
1005 * free that. Do not recurse to avoid stack overflows.
1007 * This is a slow edge condition, but necessary to
1008 * guarantee that an object can always be freed.
1010 static void
1011 prop_object_release_emergency(prop_object_t obj)
1013 struct _prop_object *po;
1014 void (*unlock)(void);
1015 prop_object_t parent = NULL;
1016 uint32_t ocnt;
1018 for (;;) {
1019 po = obj;
1020 _PROP_ASSERT(obj);
1022 if (po->po_type->pot_lock != NULL)
1023 po->po_type->pot_lock();
1025 /* Save pointerto unlock function */
1026 unlock = po->po_type->pot_unlock;
1028 /* Dance a bit to make sure we always get the non-racy ocnt */
1029 ocnt = atomic_dec_32_nv(&po->po_refcnt);
1030 ocnt++;
1031 _PROP_ASSERT(ocnt != 0);
1033 if (ocnt != 1) {
1034 if (unlock != NULL)
1035 unlock();
1036 break;
1039 _PROP_ASSERT(po->po_type);
1040 if ((po->po_type->pot_free)(NULL, &obj) ==
1041 _PROP_OBJECT_FREE_DONE) {
1042 if (unlock != NULL)
1043 unlock();
1044 break;
1047 if (unlock != NULL)
1048 unlock();
1050 parent = po;
1051 atomic_inc_32(&po->po_refcnt);
1053 _PROP_ASSERT(parent);
1054 /* One object was just freed. */
1055 po = parent;
1056 (*po->po_type->pot_emergency_free)(parent);
1060 * prop_object_release --
1061 * Decrement the reference count on an object.
1063 * Free the object if we are releasing the final
1064 * reference.
1066 void
1067 prop_object_release(prop_object_t obj)
1069 struct _prop_object *po;
1070 struct _prop_stack stack;
1071 void (*unlock)(void);
1072 int ret;
1073 uint32_t ocnt;
1075 _prop_stack_init(&stack);
1077 do {
1078 do {
1079 po = obj;
1080 _PROP_ASSERT(obj);
1082 if (po->po_type->pot_lock != NULL)
1083 po->po_type->pot_lock();
1085 /* Save pointer to object unlock function */
1086 unlock = po->po_type->pot_unlock;
1088 ocnt = atomic_dec_32_nv(&po->po_refcnt);
1089 ocnt++;
1090 _PROP_ASSERT(ocnt != 0);
1092 if (ocnt != 1) {
1093 ret = 0;
1094 if (unlock != NULL)
1095 unlock();
1096 break;
1099 ret = (po->po_type->pot_free)(&stack, &obj);
1101 if (unlock != NULL)
1102 unlock();
1104 if (ret == _PROP_OBJECT_FREE_DONE)
1105 break;
1107 atomic_inc_32(&po->po_refcnt);
1108 } while (ret == _PROP_OBJECT_FREE_RECURSE);
1109 if (ret == _PROP_OBJECT_FREE_FAILED)
1110 prop_object_release_emergency(obj);
1111 } while (_prop_stack_pop(&stack, &obj, NULL, NULL, NULL));
1115 * prop_object_type --
1116 * Return the type of an object.
1118 prop_type_t
1119 prop_object_type(prop_object_t obj)
1121 struct _prop_object *po = obj;
1123 if (obj == NULL)
1124 return (PROP_TYPE_UNKNOWN);
1126 return (po->po_type->pot_type);
1130 * prop_object_equals --
1131 * Returns true if thw two objects are equivalent.
1133 bool
1134 prop_object_equals(prop_object_t obj1, prop_object_t obj2)
1136 return (prop_object_equals_with_error(obj1, obj2, NULL));
1139 bool
1140 prop_object_equals_with_error(prop_object_t obj1, prop_object_t obj2,
1141 bool *error_flag)
1143 struct _prop_object *po1;
1144 struct _prop_object *po2;
1145 void *stored_pointer1, *stored_pointer2;
1146 prop_object_t next_obj1, next_obj2;
1147 struct _prop_stack stack;
1148 _prop_object_equals_rv_t ret;
1150 _prop_stack_init(&stack);
1151 if (error_flag)
1152 *error_flag = false;
1154 start_subtree:
1155 stored_pointer1 = NULL;
1156 stored_pointer2 = NULL;
1157 po1 = obj1;
1158 po2 = obj2;
1160 if (po1->po_type != po2->po_type)
1161 return (false);
1163 continue_subtree:
1164 ret = (*po1->po_type->pot_equals)(obj1, obj2,
1165 &stored_pointer1, &stored_pointer2,
1166 &next_obj1, &next_obj2);
1167 if (ret == _PROP_OBJECT_EQUALS_FALSE)
1168 goto finish;
1169 if (ret == _PROP_OBJECT_EQUALS_TRUE) {
1170 if (!_prop_stack_pop(&stack, &obj1, &obj2,
1171 &stored_pointer1, &stored_pointer2))
1172 return true;
1173 po1 = obj1;
1174 po2 = obj2;
1175 goto continue_subtree;
1177 _PROP_ASSERT(ret == _PROP_OBJECT_EQUALS_RECURSE);
1179 if (!_prop_stack_push(&stack, obj1, obj2,
1180 stored_pointer1, stored_pointer2)) {
1181 if (error_flag)
1182 *error_flag = true;
1183 goto finish;
1185 obj1 = next_obj1;
1186 obj2 = next_obj2;
1187 goto start_subtree;
1189 finish:
1190 while (_prop_stack_pop(&stack, &obj1, &obj2, NULL, NULL)) {
1191 po1 = obj1;
1192 (*po1->po_type->pot_equals_finish)(obj1, obj2);
1194 return (false);
1198 * prop_object_iterator_next --
1199 * Return the next item during an iteration.
1201 prop_object_t
1202 prop_object_iterator_next(prop_object_iterator_t pi)
1205 return ((*pi->pi_next_object)(pi));
1209 * prop_object_iterator_reset --
1210 * Reset the iterator to the first object so as to restart
1211 * iteration.
1213 void
1214 prop_object_iterator_reset(prop_object_iterator_t pi)
1217 (*pi->pi_reset)(pi);
1221 * prop_object_iterator_release --
1222 * Release the object iterator.
1224 void
1225 prop_object_iterator_release(prop_object_iterator_t pi)
1228 prop_object_release(pi->pi_obj);
1229 _PROP_FREE(pi, M_TEMP);