1*0a6a1f1dSLionel Sambuc /* $NetBSD: prop_object.c,v 1.30 2015/05/12 14:59:35 christos Exp $ */
26b6d114aSBen Gras
36b6d114aSBen Gras /*-
46b6d114aSBen Gras * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
56b6d114aSBen Gras * All rights reserved.
66b6d114aSBen Gras *
76b6d114aSBen Gras * This code is derived from software contributed to The NetBSD Foundation
86b6d114aSBen Gras * by Jason R. Thorpe.
96b6d114aSBen Gras *
106b6d114aSBen Gras * Redistribution and use in source and binary forms, with or without
116b6d114aSBen Gras * modification, are permitted provided that the following conditions
126b6d114aSBen Gras * are met:
136b6d114aSBen Gras * 1. Redistributions of source code must retain the above copyright
146b6d114aSBen Gras * notice, this list of conditions and the following disclaimer.
156b6d114aSBen Gras * 2. Redistributions in binary form must reproduce the above copyright
166b6d114aSBen Gras * notice, this list of conditions and the following disclaimer in the
176b6d114aSBen Gras * documentation and/or other materials provided with the distribution.
186b6d114aSBen Gras *
196b6d114aSBen Gras * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
206b6d114aSBen Gras * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
216b6d114aSBen Gras * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
226b6d114aSBen Gras * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
236b6d114aSBen Gras * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
246b6d114aSBen Gras * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
256b6d114aSBen Gras * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
266b6d114aSBen Gras * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
276b6d114aSBen Gras * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
286b6d114aSBen Gras * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
296b6d114aSBen Gras * POSSIBILITY OF SUCH DAMAGE.
306b6d114aSBen Gras */
316b6d114aSBen Gras
326b6d114aSBen Gras #include "prop_object_impl.h"
33f14fb602SLionel Sambuc #include <prop/prop_object.h>
34f14fb602SLionel Sambuc
35f14fb602SLionel Sambuc #ifdef _PROP_NEED_REFCNT_MTX
36f14fb602SLionel Sambuc static pthread_mutex_t _prop_refcnt_mtx = PTHREAD_MUTEX_INITIALIZER;
37f14fb602SLionel Sambuc #endif /* _PROP_NEED_REFCNT_MTX */
386b6d114aSBen Gras
396b6d114aSBen Gras #if !defined(_KERNEL) && !defined(_STANDALONE)
406b6d114aSBen Gras #include <sys/mman.h>
416b6d114aSBen Gras #include <sys/stat.h>
426b6d114aSBen Gras #include <errno.h>
436b6d114aSBen Gras #include <fcntl.h>
446b6d114aSBen Gras #include <limits.h>
456b6d114aSBen Gras #include <unistd.h>
46be89757eSLionel Sambuc #if defined(__minix)
476b6d114aSBen Gras #include <assert.h>
48be89757eSLionel Sambuc #endif /* defined(__minix) */
496b6d114aSBen Gras #endif
506b6d114aSBen Gras
516b6d114aSBen Gras #ifdef _STANDALONE
526b6d114aSBen Gras void *
_prop_standalone_calloc(size_t size)536b6d114aSBen Gras _prop_standalone_calloc(size_t size)
546b6d114aSBen Gras {
556b6d114aSBen Gras void *rv;
566b6d114aSBen Gras
576b6d114aSBen Gras rv = alloc(size);
586b6d114aSBen Gras if (rv != NULL)
596b6d114aSBen Gras memset(rv, 0, size);
606b6d114aSBen Gras
616b6d114aSBen Gras return (rv);
626b6d114aSBen Gras }
636b6d114aSBen Gras
646b6d114aSBen Gras void *
_prop_standalone_realloc(void * v,size_t size)656b6d114aSBen Gras _prop_standalone_realloc(void *v, size_t size)
666b6d114aSBen Gras {
676b6d114aSBen Gras void *rv;
686b6d114aSBen Gras
696b6d114aSBen Gras rv = alloc(size);
706b6d114aSBen Gras if (rv != NULL) {
716b6d114aSBen Gras memcpy(rv, v, size); /* XXX */
726b6d114aSBen Gras dealloc(v, 0); /* XXX */
736b6d114aSBen Gras }
746b6d114aSBen Gras
756b6d114aSBen Gras return (rv);
766b6d114aSBen Gras }
776b6d114aSBen Gras #endif /* _STANDALONE */
786b6d114aSBen Gras
796b6d114aSBen Gras /*
806b6d114aSBen Gras * _prop_object_init --
816b6d114aSBen Gras * Initialize an object. Called when sub-classes create
826b6d114aSBen Gras * an instance.
836b6d114aSBen Gras */
846b6d114aSBen Gras void
_prop_object_init(struct _prop_object * po,const struct _prop_object_type * pot)856b6d114aSBen Gras _prop_object_init(struct _prop_object *po, const struct _prop_object_type *pot)
866b6d114aSBen Gras {
876b6d114aSBen Gras
886b6d114aSBen Gras po->po_type = pot;
896b6d114aSBen Gras po->po_refcnt = 1;
906b6d114aSBen Gras }
916b6d114aSBen Gras
926b6d114aSBen Gras /*
936b6d114aSBen Gras * _prop_object_fini --
946b6d114aSBen Gras * Finalize an object. Called when sub-classes destroy
956b6d114aSBen Gras * an instance.
966b6d114aSBen Gras */
976b6d114aSBen Gras /*ARGSUSED*/
986b6d114aSBen Gras void
_prop_object_fini(struct _prop_object * po _PROP_ARG_UNUSED)996b6d114aSBen Gras _prop_object_fini(struct _prop_object *po _PROP_ARG_UNUSED)
1006b6d114aSBen Gras {
1016b6d114aSBen Gras /* Nothing to do, currently. */
1026b6d114aSBen Gras }
1036b6d114aSBen Gras
1046b6d114aSBen Gras /*
1056b6d114aSBen Gras * _prop_object_externalize_start_tag --
1066b6d114aSBen Gras * Append an XML-style start tag to the externalize buffer.
1076b6d114aSBen Gras */
1086b6d114aSBen Gras bool
_prop_object_externalize_start_tag(struct _prop_object_externalize_context * ctx,const char * tag)1096b6d114aSBen Gras _prop_object_externalize_start_tag(
1106b6d114aSBen Gras struct _prop_object_externalize_context *ctx, const char *tag)
1116b6d114aSBen Gras {
1126b6d114aSBen Gras unsigned int i;
1136b6d114aSBen Gras
1146b6d114aSBen Gras for (i = 0; i < ctx->poec_depth; i++) {
1156b6d114aSBen Gras if (_prop_object_externalize_append_char(ctx, '\t') == false)
1166b6d114aSBen Gras return (false);
1176b6d114aSBen Gras }
1186b6d114aSBen Gras if (_prop_object_externalize_append_char(ctx, '<') == false ||
1196b6d114aSBen Gras _prop_object_externalize_append_cstring(ctx, tag) == false ||
1206b6d114aSBen Gras _prop_object_externalize_append_char(ctx, '>') == false)
1216b6d114aSBen Gras return (false);
1226b6d114aSBen Gras
1236b6d114aSBen Gras return (true);
1246b6d114aSBen Gras }
1256b6d114aSBen Gras
1266b6d114aSBen Gras /*
1276b6d114aSBen Gras * _prop_object_externalize_end_tag --
1286b6d114aSBen Gras * Append an XML-style end tag to the externalize buffer.
1296b6d114aSBen Gras */
1306b6d114aSBen Gras bool
_prop_object_externalize_end_tag(struct _prop_object_externalize_context * ctx,const char * tag)1316b6d114aSBen Gras _prop_object_externalize_end_tag(
1326b6d114aSBen Gras struct _prop_object_externalize_context *ctx, const char *tag)
1336b6d114aSBen Gras {
1346b6d114aSBen Gras
1356b6d114aSBen Gras if (_prop_object_externalize_append_char(ctx, '<') == false ||
1366b6d114aSBen Gras _prop_object_externalize_append_char(ctx, '/') == false ||
1376b6d114aSBen Gras _prop_object_externalize_append_cstring(ctx, tag) == false ||
1386b6d114aSBen Gras _prop_object_externalize_append_char(ctx, '>') == false ||
1396b6d114aSBen Gras _prop_object_externalize_append_char(ctx, '\n') == false)
1406b6d114aSBen Gras return (false);
1416b6d114aSBen Gras
1426b6d114aSBen Gras return (true);
1436b6d114aSBen Gras }
1446b6d114aSBen Gras
1456b6d114aSBen Gras /*
1466b6d114aSBen Gras * _prop_object_externalize_empty_tag --
1476b6d114aSBen Gras * Append an XML-style empty tag to the externalize buffer.
1486b6d114aSBen Gras */
1496b6d114aSBen Gras bool
_prop_object_externalize_empty_tag(struct _prop_object_externalize_context * ctx,const char * tag)1506b6d114aSBen Gras _prop_object_externalize_empty_tag(
1516b6d114aSBen Gras struct _prop_object_externalize_context *ctx, const char *tag)
1526b6d114aSBen Gras {
1536b6d114aSBen Gras unsigned int i;
1546b6d114aSBen Gras
1556b6d114aSBen Gras for (i = 0; i < ctx->poec_depth; i++) {
1566b6d114aSBen Gras if (_prop_object_externalize_append_char(ctx, '\t') == false)
1576b6d114aSBen Gras return (false);
1586b6d114aSBen Gras }
1596b6d114aSBen Gras
1606b6d114aSBen Gras if (_prop_object_externalize_append_char(ctx, '<') == false ||
1616b6d114aSBen Gras _prop_object_externalize_append_cstring(ctx, tag) == false ||
1626b6d114aSBen Gras _prop_object_externalize_append_char(ctx, '/') == false ||
1636b6d114aSBen Gras _prop_object_externalize_append_char(ctx, '>') == false ||
1646b6d114aSBen Gras _prop_object_externalize_append_char(ctx, '\n') == false)
1656b6d114aSBen Gras return (false);
1666b6d114aSBen Gras
1676b6d114aSBen Gras return (true);
1686b6d114aSBen Gras }
1696b6d114aSBen Gras
1706b6d114aSBen Gras /*
1716b6d114aSBen Gras * _prop_object_externalize_append_cstring --
1726b6d114aSBen Gras * Append a C string to the externalize buffer.
1736b6d114aSBen Gras */
1746b6d114aSBen Gras bool
_prop_object_externalize_append_cstring(struct _prop_object_externalize_context * ctx,const char * cp)1756b6d114aSBen Gras _prop_object_externalize_append_cstring(
1766b6d114aSBen Gras struct _prop_object_externalize_context *ctx, const char *cp)
1776b6d114aSBen Gras {
1786b6d114aSBen Gras
1796b6d114aSBen Gras while (*cp != '\0') {
1806b6d114aSBen Gras if (_prop_object_externalize_append_char(ctx,
1816b6d114aSBen Gras (unsigned char) *cp) == false)
1826b6d114aSBen Gras return (false);
1836b6d114aSBen Gras cp++;
1846b6d114aSBen Gras }
1856b6d114aSBen Gras
1866b6d114aSBen Gras return (true);
1876b6d114aSBen Gras }
1886b6d114aSBen Gras
1896b6d114aSBen Gras /*
1906b6d114aSBen Gras * _prop_object_externalize_append_encoded_cstring --
1916b6d114aSBen Gras * Append an encoded C string to the externalize buffer.
1926b6d114aSBen Gras */
1936b6d114aSBen Gras bool
_prop_object_externalize_append_encoded_cstring(struct _prop_object_externalize_context * ctx,const char * cp)1946b6d114aSBen Gras _prop_object_externalize_append_encoded_cstring(
1956b6d114aSBen Gras struct _prop_object_externalize_context *ctx, const char *cp)
1966b6d114aSBen Gras {
1976b6d114aSBen Gras
1986b6d114aSBen Gras while (*cp != '\0') {
1996b6d114aSBen Gras switch (*cp) {
2006b6d114aSBen Gras case '<':
2016b6d114aSBen Gras if (_prop_object_externalize_append_cstring(ctx,
2026b6d114aSBen Gras "<") == false)
2036b6d114aSBen Gras return (false);
2046b6d114aSBen Gras break;
2056b6d114aSBen Gras case '>':
2066b6d114aSBen Gras if (_prop_object_externalize_append_cstring(ctx,
2076b6d114aSBen Gras ">") == false)
2086b6d114aSBen Gras return (false);
2096b6d114aSBen Gras break;
2106b6d114aSBen Gras case '&':
2116b6d114aSBen Gras if (_prop_object_externalize_append_cstring(ctx,
2126b6d114aSBen Gras "&") == false)
2136b6d114aSBen Gras return (false);
2146b6d114aSBen Gras break;
2156b6d114aSBen Gras default:
2166b6d114aSBen Gras if (_prop_object_externalize_append_char(ctx,
2176b6d114aSBen Gras (unsigned char) *cp) == false)
2186b6d114aSBen Gras return (false);
2196b6d114aSBen Gras break;
2206b6d114aSBen Gras }
2216b6d114aSBen Gras cp++;
2226b6d114aSBen Gras }
2236b6d114aSBen Gras
2246b6d114aSBen Gras return (true);
2256b6d114aSBen Gras }
2266b6d114aSBen Gras
2276b6d114aSBen Gras #define BUF_EXPAND 256
2286b6d114aSBen Gras
2296b6d114aSBen Gras /*
2306b6d114aSBen Gras * _prop_object_externalize_append_char --
2316b6d114aSBen Gras * Append a single character to the externalize buffer.
2326b6d114aSBen Gras */
2336b6d114aSBen Gras bool
_prop_object_externalize_append_char(struct _prop_object_externalize_context * ctx,unsigned char c)2346b6d114aSBen Gras _prop_object_externalize_append_char(
2356b6d114aSBen Gras struct _prop_object_externalize_context *ctx, unsigned char c)
2366b6d114aSBen Gras {
2376b6d114aSBen Gras
2386b6d114aSBen Gras _PROP_ASSERT(ctx->poec_capacity != 0);
2396b6d114aSBen Gras _PROP_ASSERT(ctx->poec_buf != NULL);
2406b6d114aSBen Gras _PROP_ASSERT(ctx->poec_len <= ctx->poec_capacity);
2416b6d114aSBen Gras
2426b6d114aSBen Gras if (ctx->poec_len == ctx->poec_capacity) {
2436b6d114aSBen Gras char *cp = _PROP_REALLOC(ctx->poec_buf,
2446b6d114aSBen Gras ctx->poec_capacity + BUF_EXPAND,
2456b6d114aSBen Gras M_TEMP);
2466b6d114aSBen Gras if (cp == NULL)
2476b6d114aSBen Gras return (false);
2486b6d114aSBen Gras ctx->poec_capacity = ctx->poec_capacity + BUF_EXPAND;
2496b6d114aSBen Gras ctx->poec_buf = cp;
2506b6d114aSBen Gras }
2516b6d114aSBen Gras
2526b6d114aSBen Gras ctx->poec_buf[ctx->poec_len++] = c;
2536b6d114aSBen Gras
2546b6d114aSBen Gras return (true);
2556b6d114aSBen Gras }
2566b6d114aSBen Gras
2576b6d114aSBen Gras /*
2586b6d114aSBen Gras * _prop_object_externalize_header --
2596b6d114aSBen Gras * Append the standard XML header to the externalize buffer.
2606b6d114aSBen Gras */
2616b6d114aSBen Gras bool
_prop_object_externalize_header(struct _prop_object_externalize_context * ctx)2626b6d114aSBen Gras _prop_object_externalize_header(struct _prop_object_externalize_context *ctx)
2636b6d114aSBen Gras {
2646b6d114aSBen Gras static const char _plist_xml_header[] =
2656b6d114aSBen Gras "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
2666b6d114aSBen Gras "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n";
2676b6d114aSBen Gras
2686b6d114aSBen Gras if (_prop_object_externalize_append_cstring(ctx,
2696b6d114aSBen Gras _plist_xml_header) == false ||
2706b6d114aSBen Gras _prop_object_externalize_start_tag(ctx,
2716b6d114aSBen Gras "plist version=\"1.0\"") == false ||
2726b6d114aSBen Gras _prop_object_externalize_append_char(ctx, '\n') == false)
2736b6d114aSBen Gras return (false);
2746b6d114aSBen Gras
2756b6d114aSBen Gras return (true);
2766b6d114aSBen Gras }
2776b6d114aSBen Gras
2786b6d114aSBen Gras /*
2796b6d114aSBen Gras * _prop_object_externalize_footer --
2806b6d114aSBen Gras * Append the standard XML footer to the externalize buffer. This
2816b6d114aSBen Gras * also NUL-terminates the buffer.
2826b6d114aSBen Gras */
2836b6d114aSBen Gras bool
_prop_object_externalize_footer(struct _prop_object_externalize_context * ctx)2846b6d114aSBen Gras _prop_object_externalize_footer(struct _prop_object_externalize_context *ctx)
2856b6d114aSBen Gras {
2866b6d114aSBen Gras
2876b6d114aSBen Gras if (_prop_object_externalize_end_tag(ctx, "plist") == false ||
2886b6d114aSBen Gras _prop_object_externalize_append_char(ctx, '\0') == false)
2896b6d114aSBen Gras return (false);
2906b6d114aSBen Gras
2916b6d114aSBen Gras return (true);
2926b6d114aSBen Gras }
2936b6d114aSBen Gras
2946b6d114aSBen Gras /*
2956b6d114aSBen Gras * _prop_object_externalize_context_alloc --
2966b6d114aSBen Gras * Allocate an externalize context.
2976b6d114aSBen Gras */
2986b6d114aSBen Gras struct _prop_object_externalize_context *
_prop_object_externalize_context_alloc(void)2996b6d114aSBen Gras _prop_object_externalize_context_alloc(void)
3006b6d114aSBen Gras {
3016b6d114aSBen Gras struct _prop_object_externalize_context *ctx;
3026b6d114aSBen Gras
3036b6d114aSBen Gras ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP);
3046b6d114aSBen Gras if (ctx != NULL) {
3056b6d114aSBen Gras ctx->poec_buf = _PROP_MALLOC(BUF_EXPAND, M_TEMP);
3066b6d114aSBen Gras if (ctx->poec_buf == NULL) {
3076b6d114aSBen Gras _PROP_FREE(ctx, M_TEMP);
3086b6d114aSBen Gras return (NULL);
3096b6d114aSBen Gras }
3106b6d114aSBen Gras ctx->poec_len = 0;
3116b6d114aSBen Gras ctx->poec_capacity = BUF_EXPAND;
3126b6d114aSBen Gras ctx->poec_depth = 0;
3136b6d114aSBen Gras }
3146b6d114aSBen Gras return (ctx);
3156b6d114aSBen Gras }
3166b6d114aSBen Gras
3176b6d114aSBen Gras /*
3186b6d114aSBen Gras * _prop_object_externalize_context_free --
3196b6d114aSBen Gras * Free an externalize context.
3206b6d114aSBen Gras */
3216b6d114aSBen Gras void
_prop_object_externalize_context_free(struct _prop_object_externalize_context * ctx)3226b6d114aSBen Gras _prop_object_externalize_context_free(
3236b6d114aSBen Gras struct _prop_object_externalize_context *ctx)
3246b6d114aSBen Gras {
3256b6d114aSBen Gras
3266b6d114aSBen Gras /* Buffer is always freed by the caller. */
3276b6d114aSBen Gras _PROP_FREE(ctx, M_TEMP);
3286b6d114aSBen Gras }
3296b6d114aSBen Gras
3306b6d114aSBen Gras /*
3316b6d114aSBen Gras * _prop_object_internalize_skip_comment --
3326b6d114aSBen Gras * Skip the body and end tag of a comment.
3336b6d114aSBen Gras */
3346b6d114aSBen Gras static bool
_prop_object_internalize_skip_comment(struct _prop_object_internalize_context * ctx)3356b6d114aSBen Gras _prop_object_internalize_skip_comment(
3366b6d114aSBen Gras struct _prop_object_internalize_context *ctx)
3376b6d114aSBen Gras {
3386b6d114aSBen Gras const char *cp = ctx->poic_cp;
3396b6d114aSBen Gras
3406b6d114aSBen Gras while (!_PROP_EOF(*cp)) {
3416b6d114aSBen Gras if (cp[0] == '-' &&
3426b6d114aSBen Gras cp[1] == '-' &&
3436b6d114aSBen Gras cp[2] == '>') {
3446b6d114aSBen Gras ctx->poic_cp = cp + 3;
3456b6d114aSBen Gras return (true);
3466b6d114aSBen Gras }
3476b6d114aSBen Gras cp++;
3486b6d114aSBen Gras }
3496b6d114aSBen Gras
3506b6d114aSBen Gras return (false); /* ran out of buffer */
3516b6d114aSBen Gras }
3526b6d114aSBen Gras
3536b6d114aSBen Gras /*
3546b6d114aSBen Gras * _prop_object_internalize_find_tag --
3556b6d114aSBen Gras * Find the next tag in an XML stream. Optionally compare the found
3566b6d114aSBen Gras * tag to an expected tag name. State of the context is undefined
3576b6d114aSBen Gras * if this routine returns false. Upon success, the context points
3586b6d114aSBen Gras * to the first octet after the tag.
3596b6d114aSBen Gras */
3606b6d114aSBen Gras bool
_prop_object_internalize_find_tag(struct _prop_object_internalize_context * ctx,const char * tag,_prop_tag_type_t type)3616b6d114aSBen Gras _prop_object_internalize_find_tag(struct _prop_object_internalize_context *ctx,
3626b6d114aSBen Gras const char *tag, _prop_tag_type_t type)
3636b6d114aSBen Gras {
3646b6d114aSBen Gras const char *cp;
3656b6d114aSBen Gras size_t taglen;
3666b6d114aSBen Gras
3676b6d114aSBen Gras if (tag != NULL)
3686b6d114aSBen Gras taglen = strlen(tag);
3696b6d114aSBen Gras else
3706b6d114aSBen Gras taglen = 0;
3716b6d114aSBen Gras
3726b6d114aSBen Gras start_over:
3736b6d114aSBen Gras cp = ctx->poic_cp;
3746b6d114aSBen Gras
3756b6d114aSBen Gras /*
3766b6d114aSBen Gras * Find the start of the tag.
3776b6d114aSBen Gras */
3786b6d114aSBen Gras while (_PROP_ISSPACE(*cp))
3796b6d114aSBen Gras cp++;
3806b6d114aSBen Gras if (_PROP_EOF(*cp))
3816b6d114aSBen Gras return (false);
3826b6d114aSBen Gras
3836b6d114aSBen Gras if (*cp != '<')
3846b6d114aSBen Gras return (false);
3856b6d114aSBen Gras
3866b6d114aSBen Gras ctx->poic_tag_start = cp++;
3876b6d114aSBen Gras if (_PROP_EOF(*cp))
3886b6d114aSBen Gras return (false);
3896b6d114aSBen Gras
3906b6d114aSBen Gras if (*cp == '!') {
3916b6d114aSBen Gras if (cp[1] != '-' || cp[2] != '-')
3926b6d114aSBen Gras return (false);
3936b6d114aSBen Gras /*
3946b6d114aSBen Gras * Comment block -- only allowed if we are allowed to
3956b6d114aSBen Gras * return a start tag.
3966b6d114aSBen Gras */
3976b6d114aSBen Gras if (type == _PROP_TAG_TYPE_END)
3986b6d114aSBen Gras return (false);
3996b6d114aSBen Gras ctx->poic_cp = cp + 3;
4006b6d114aSBen Gras if (_prop_object_internalize_skip_comment(ctx) == false)
4016b6d114aSBen Gras return (false);
4026b6d114aSBen Gras goto start_over;
4036b6d114aSBen Gras }
4046b6d114aSBen Gras
4056b6d114aSBen Gras if (*cp == '/') {
4066b6d114aSBen Gras if (type != _PROP_TAG_TYPE_END &&
4076b6d114aSBen Gras type != _PROP_TAG_TYPE_EITHER)
4086b6d114aSBen Gras return (false);
4096b6d114aSBen Gras cp++;
4106b6d114aSBen Gras if (_PROP_EOF(*cp))
4116b6d114aSBen Gras return (false);
4126b6d114aSBen Gras ctx->poic_tag_type = _PROP_TAG_TYPE_END;
4136b6d114aSBen Gras } else {
4146b6d114aSBen Gras if (type != _PROP_TAG_TYPE_START &&
4156b6d114aSBen Gras type != _PROP_TAG_TYPE_EITHER)
4166b6d114aSBen Gras return (false);
4176b6d114aSBen Gras ctx->poic_tag_type = _PROP_TAG_TYPE_START;
4186b6d114aSBen Gras }
4196b6d114aSBen Gras
4206b6d114aSBen Gras ctx->poic_tagname = cp;
4216b6d114aSBen Gras
422*0a6a1f1dSLionel Sambuc while (!_PROP_ISSPACE(*cp) && *cp != '/' && *cp != '>') {
4236b6d114aSBen Gras if (_PROP_EOF(*cp))
4246b6d114aSBen Gras return (false);
425*0a6a1f1dSLionel Sambuc cp++;
426*0a6a1f1dSLionel Sambuc }
4276b6d114aSBen Gras
4286b6d114aSBen Gras ctx->poic_tagname_len = cp - ctx->poic_tagname;
4296b6d114aSBen Gras
4306b6d114aSBen Gras /* Make sure this is the tag we're looking for. */
4316b6d114aSBen Gras if (tag != NULL &&
4326b6d114aSBen Gras (taglen != ctx->poic_tagname_len ||
4336b6d114aSBen Gras memcmp(tag, ctx->poic_tagname, taglen) != 0))
4346b6d114aSBen Gras return (false);
4356b6d114aSBen Gras
4366b6d114aSBen Gras /* Check for empty tag. */
4376b6d114aSBen Gras if (*cp == '/') {
4386b6d114aSBen Gras if (ctx->poic_tag_type != _PROP_TAG_TYPE_START)
4396b6d114aSBen Gras return(false); /* only valid on start tags */
4406b6d114aSBen Gras ctx->poic_is_empty_element = true;
4416b6d114aSBen Gras cp++;
4426b6d114aSBen Gras if (_PROP_EOF(*cp) || *cp != '>')
4436b6d114aSBen Gras return (false);
4446b6d114aSBen Gras } else
4456b6d114aSBen Gras ctx->poic_is_empty_element = false;
4466b6d114aSBen Gras
4476b6d114aSBen Gras /* Easy case of no arguments. */
4486b6d114aSBen Gras if (*cp == '>') {
4496b6d114aSBen Gras ctx->poic_tagattr = NULL;
4506b6d114aSBen Gras ctx->poic_tagattr_len = 0;
4516b6d114aSBen Gras ctx->poic_tagattrval = NULL;
4526b6d114aSBen Gras ctx->poic_tagattrval_len = 0;
4536b6d114aSBen Gras ctx->poic_cp = cp + 1;
4546b6d114aSBen Gras return (true);
4556b6d114aSBen Gras }
4566b6d114aSBen Gras
4576b6d114aSBen Gras _PROP_ASSERT(!_PROP_EOF(*cp));
4586b6d114aSBen Gras cp++;
4596b6d114aSBen Gras if (_PROP_EOF(*cp))
4606b6d114aSBen Gras return (false);
4616b6d114aSBen Gras
4626b6d114aSBen Gras while (_PROP_ISSPACE(*cp))
4636b6d114aSBen Gras cp++;
4646b6d114aSBen Gras if (_PROP_EOF(*cp))
4656b6d114aSBen Gras return (false);
4666b6d114aSBen Gras
4676b6d114aSBen Gras ctx->poic_tagattr = cp;
4686b6d114aSBen Gras
469*0a6a1f1dSLionel Sambuc while (!_PROP_ISSPACE(*cp) && *cp != '=') {
4706b6d114aSBen Gras if (_PROP_EOF(*cp))
4716b6d114aSBen Gras return (false);
472*0a6a1f1dSLionel Sambuc cp++;
473*0a6a1f1dSLionel Sambuc }
4746b6d114aSBen Gras
4756b6d114aSBen Gras ctx->poic_tagattr_len = cp - ctx->poic_tagattr;
4766b6d114aSBen Gras
4776b6d114aSBen Gras cp++;
4786b6d114aSBen Gras if (*cp != '\"')
4796b6d114aSBen Gras return (false);
4806b6d114aSBen Gras cp++;
4816b6d114aSBen Gras if (_PROP_EOF(*cp))
4826b6d114aSBen Gras return (false);
4836b6d114aSBen Gras
4846b6d114aSBen Gras ctx->poic_tagattrval = cp;
485*0a6a1f1dSLionel Sambuc while (*cp != '\"') {
4866b6d114aSBen Gras if (_PROP_EOF(*cp))
4876b6d114aSBen Gras return (false);
488*0a6a1f1dSLionel Sambuc cp++;
489*0a6a1f1dSLionel Sambuc }
4906b6d114aSBen Gras ctx->poic_tagattrval_len = cp - ctx->poic_tagattrval;
4916b6d114aSBen Gras
4926b6d114aSBen Gras cp++;
4936b6d114aSBen Gras if (*cp != '>')
4946b6d114aSBen Gras return (false);
4956b6d114aSBen Gras
4966b6d114aSBen Gras ctx->poic_cp = cp + 1;
4976b6d114aSBen Gras return (true);
4986b6d114aSBen Gras }
4996b6d114aSBen Gras
5006b6d114aSBen Gras /*
5016b6d114aSBen Gras * _prop_object_internalize_decode_string --
5026b6d114aSBen Gras * Decode an encoded string.
5036b6d114aSBen Gras */
5046b6d114aSBen Gras bool
_prop_object_internalize_decode_string(struct _prop_object_internalize_context * ctx,char * target,size_t targsize,size_t * sizep,const char ** cpp)5056b6d114aSBen Gras _prop_object_internalize_decode_string(
5066b6d114aSBen Gras struct _prop_object_internalize_context *ctx,
5076b6d114aSBen Gras char *target, size_t targsize, size_t *sizep,
5086b6d114aSBen Gras const char **cpp)
5096b6d114aSBen Gras {
5106b6d114aSBen Gras const char *src;
5116b6d114aSBen Gras size_t tarindex;
5126b6d114aSBen Gras char c;
5136b6d114aSBen Gras
5146b6d114aSBen Gras tarindex = 0;
5156b6d114aSBen Gras src = ctx->poic_cp;
5166b6d114aSBen Gras
5176b6d114aSBen Gras for (;;) {
5186b6d114aSBen Gras if (_PROP_EOF(*src))
5196b6d114aSBen Gras return (false);
5206b6d114aSBen Gras if (*src == '<') {
5216b6d114aSBen Gras break;
5226b6d114aSBen Gras }
5236b6d114aSBen Gras
5246b6d114aSBen Gras if ((c = *src) == '&') {
5256b6d114aSBen Gras if (src[1] == 'a' &&
5266b6d114aSBen Gras src[2] == 'm' &&
5276b6d114aSBen Gras src[3] == 'p' &&
5286b6d114aSBen Gras src[4] == ';') {
5296b6d114aSBen Gras c = '&';
5306b6d114aSBen Gras src += 5;
5316b6d114aSBen Gras } else if (src[1] == 'l' &&
5326b6d114aSBen Gras src[2] == 't' &&
5336b6d114aSBen Gras src[3] == ';') {
5346b6d114aSBen Gras c = '<';
5356b6d114aSBen Gras src += 4;
5366b6d114aSBen Gras } else if (src[1] == 'g' &&
5376b6d114aSBen Gras src[2] == 't' &&
5386b6d114aSBen Gras src[3] == ';') {
5396b6d114aSBen Gras c = '>';
5406b6d114aSBen Gras src += 4;
5416b6d114aSBen Gras } else if (src[1] == 'a' &&
5426b6d114aSBen Gras src[2] == 'p' &&
5436b6d114aSBen Gras src[3] == 'o' &&
5446b6d114aSBen Gras src[4] == 's' &&
5456b6d114aSBen Gras src[5] == ';') {
5466b6d114aSBen Gras c = '\'';
5476b6d114aSBen Gras src += 6;
5486b6d114aSBen Gras } else if (src[1] == 'q' &&
5496b6d114aSBen Gras src[2] == 'u' &&
5506b6d114aSBen Gras src[3] == 'o' &&
5516b6d114aSBen Gras src[4] == 't' &&
5526b6d114aSBen Gras src[5] == ';') {
5536b6d114aSBen Gras c = '\"';
5546b6d114aSBen Gras src += 6;
5556b6d114aSBen Gras } else
5566b6d114aSBen Gras return (false);
5576b6d114aSBen Gras } else
5586b6d114aSBen Gras src++;
5596b6d114aSBen Gras if (target) {
5606b6d114aSBen Gras if (tarindex >= targsize)
5616b6d114aSBen Gras return (false);
5626b6d114aSBen Gras target[tarindex] = c;
5636b6d114aSBen Gras }
5646b6d114aSBen Gras tarindex++;
5656b6d114aSBen Gras }
5666b6d114aSBen Gras
5676b6d114aSBen Gras _PROP_ASSERT(*src == '<');
5686b6d114aSBen Gras if (sizep != NULL)
5696b6d114aSBen Gras *sizep = tarindex;
5706b6d114aSBen Gras if (cpp != NULL)
5716b6d114aSBen Gras *cpp = src;
5726b6d114aSBen Gras
5736b6d114aSBen Gras return (true);
5746b6d114aSBen Gras }
5756b6d114aSBen Gras
5766b6d114aSBen Gras /*
5776b6d114aSBen Gras * _prop_object_internalize_match --
5786b6d114aSBen Gras * Returns true if the two character streams match.
5796b6d114aSBen Gras */
5806b6d114aSBen Gras bool
_prop_object_internalize_match(const char * str1,size_t len1,const char * str2,size_t len2)5816b6d114aSBen Gras _prop_object_internalize_match(const char *str1, size_t len1,
5826b6d114aSBen Gras const char *str2, size_t len2)
5836b6d114aSBen Gras {
5846b6d114aSBen Gras
5856b6d114aSBen Gras return (len1 == len2 && memcmp(str1, str2, len1) == 0);
5866b6d114aSBen Gras }
5876b6d114aSBen Gras
5886b6d114aSBen Gras #define INTERNALIZER(t, f) \
5896b6d114aSBen Gras { t, sizeof(t) - 1, f }
5906b6d114aSBen Gras
5916b6d114aSBen Gras static const struct _prop_object_internalizer {
5926b6d114aSBen Gras const char *poi_tag;
5936b6d114aSBen Gras size_t poi_taglen;
5946b6d114aSBen Gras prop_object_internalizer_t poi_intern;
5956b6d114aSBen Gras } _prop_object_internalizer_table[] = {
5966b6d114aSBen Gras INTERNALIZER("array", _prop_array_internalize),
5976b6d114aSBen Gras
5986b6d114aSBen Gras INTERNALIZER("true", _prop_bool_internalize),
5996b6d114aSBen Gras INTERNALIZER("false", _prop_bool_internalize),
6006b6d114aSBen Gras
6016b6d114aSBen Gras INTERNALIZER("data", _prop_data_internalize),
6026b6d114aSBen Gras
6036b6d114aSBen Gras INTERNALIZER("dict", _prop_dictionary_internalize),
6046b6d114aSBen Gras
6056b6d114aSBen Gras INTERNALIZER("integer", _prop_number_internalize),
6066b6d114aSBen Gras
6076b6d114aSBen Gras INTERNALIZER("string", _prop_string_internalize),
6086b6d114aSBen Gras
6096b6d114aSBen Gras { 0, 0, NULL }
6106b6d114aSBen Gras };
6116b6d114aSBen Gras
6126b6d114aSBen Gras #undef INTERNALIZER
6136b6d114aSBen Gras
6146b6d114aSBen Gras /*
6156b6d114aSBen Gras * _prop_object_internalize_by_tag --
6166b6d114aSBen Gras * Determine the object type from the tag in the context and
6176b6d114aSBen Gras * internalize it.
6186b6d114aSBen Gras */
6196b6d114aSBen Gras prop_object_t
_prop_object_internalize_by_tag(struct _prop_object_internalize_context * ctx)6206b6d114aSBen Gras _prop_object_internalize_by_tag(struct _prop_object_internalize_context *ctx)
6216b6d114aSBen Gras {
6226b6d114aSBen Gras const struct _prop_object_internalizer *poi;
6236b6d114aSBen Gras prop_object_t obj, parent_obj;
6246b6d114aSBen Gras void *data, *iter;
6256b6d114aSBen Gras prop_object_internalizer_continue_t iter_func;
6266b6d114aSBen Gras struct _prop_stack stack;
6276b6d114aSBen Gras
6286b6d114aSBen Gras _prop_stack_init(&stack);
6296b6d114aSBen Gras
6306b6d114aSBen Gras match_start:
6316b6d114aSBen Gras for (poi = _prop_object_internalizer_table;
6326b6d114aSBen Gras poi->poi_tag != NULL; poi++) {
6336b6d114aSBen Gras if (_prop_object_internalize_match(ctx->poic_tagname,
6346b6d114aSBen Gras ctx->poic_tagname_len,
6356b6d114aSBen Gras poi->poi_tag,
6366b6d114aSBen Gras poi->poi_taglen))
6376b6d114aSBen Gras break;
6386b6d114aSBen Gras }
6396b6d114aSBen Gras if ((poi == NULL) || (poi->poi_tag == NULL)) {
6406b6d114aSBen Gras while (_prop_stack_pop(&stack, &obj, &iter, &data, NULL)) {
6416b6d114aSBen Gras iter_func = (prop_object_internalizer_continue_t)iter;
6426b6d114aSBen Gras (*iter_func)(&stack, &obj, ctx, data, NULL);
6436b6d114aSBen Gras }
6446b6d114aSBen Gras
6456b6d114aSBen Gras return (NULL);
6466b6d114aSBen Gras }
6476b6d114aSBen Gras
6486b6d114aSBen Gras obj = NULL;
6496b6d114aSBen Gras if (!(*poi->poi_intern)(&stack, &obj, ctx))
6506b6d114aSBen Gras goto match_start;
6516b6d114aSBen Gras
6526b6d114aSBen Gras parent_obj = obj;
6536b6d114aSBen Gras while (_prop_stack_pop(&stack, &parent_obj, &iter, &data, NULL)) {
6546b6d114aSBen Gras iter_func = (prop_object_internalizer_continue_t)iter;
6556b6d114aSBen Gras if (!(*iter_func)(&stack, &parent_obj, ctx, data, obj))
6566b6d114aSBen Gras goto match_start;
6576b6d114aSBen Gras obj = parent_obj;
6586b6d114aSBen Gras }
6596b6d114aSBen Gras
6606b6d114aSBen Gras return (parent_obj);
6616b6d114aSBen Gras }
6626b6d114aSBen Gras
6636b6d114aSBen Gras prop_object_t
_prop_generic_internalize(const char * xml,const char * master_tag)6646b6d114aSBen Gras _prop_generic_internalize(const char *xml, const char *master_tag)
6656b6d114aSBen Gras {
6666b6d114aSBen Gras prop_object_t obj = NULL;
6676b6d114aSBen Gras struct _prop_object_internalize_context *ctx;
6686b6d114aSBen Gras
6696b6d114aSBen Gras ctx = _prop_object_internalize_context_alloc(xml);
6706b6d114aSBen Gras if (ctx == NULL)
6716b6d114aSBen Gras return (NULL);
6726b6d114aSBen Gras
6736b6d114aSBen Gras /* We start with a <plist> tag. */
6746b6d114aSBen Gras if (_prop_object_internalize_find_tag(ctx, "plist",
6756b6d114aSBen Gras _PROP_TAG_TYPE_START) == false)
6766b6d114aSBen Gras goto out;
6776b6d114aSBen Gras
6786b6d114aSBen Gras /* Plist elements cannot be empty. */
6796b6d114aSBen Gras if (ctx->poic_is_empty_element)
6806b6d114aSBen Gras goto out;
6816b6d114aSBen Gras
6826b6d114aSBen Gras /*
6836b6d114aSBen Gras * We don't understand any plist attributes, but Apple XML
6846b6d114aSBen Gras * property lists often have a "version" attribute. If we
6856b6d114aSBen Gras * see that one, we simply ignore it.
6866b6d114aSBen Gras */
6876b6d114aSBen Gras if (ctx->poic_tagattr != NULL &&
6886b6d114aSBen Gras !_PROP_TAGATTR_MATCH(ctx, "version"))
6896b6d114aSBen Gras goto out;
6906b6d114aSBen Gras
6916b6d114aSBen Gras /* Next we expect to see opening master_tag. */
6926b6d114aSBen Gras if (_prop_object_internalize_find_tag(ctx, master_tag,
6936b6d114aSBen Gras _PROP_TAG_TYPE_START) == false)
6946b6d114aSBen Gras goto out;
6956b6d114aSBen Gras
6966b6d114aSBen Gras obj = _prop_object_internalize_by_tag(ctx);
6976b6d114aSBen Gras if (obj == NULL)
6986b6d114aSBen Gras goto out;
6996b6d114aSBen Gras
7006b6d114aSBen Gras /*
7016b6d114aSBen Gras * We've advanced past the closing master_tag.
7026b6d114aSBen Gras * Now we want </plist>.
7036b6d114aSBen Gras */
7046b6d114aSBen Gras if (_prop_object_internalize_find_tag(ctx, "plist",
7056b6d114aSBen Gras _PROP_TAG_TYPE_END) == false) {
7066b6d114aSBen Gras prop_object_release(obj);
7076b6d114aSBen Gras obj = NULL;
7086b6d114aSBen Gras }
7096b6d114aSBen Gras
7106b6d114aSBen Gras out:
7116b6d114aSBen Gras _prop_object_internalize_context_free(ctx);
7126b6d114aSBen Gras return (obj);
7136b6d114aSBen Gras }
7146b6d114aSBen Gras
7156b6d114aSBen Gras /*
7166b6d114aSBen Gras * _prop_object_internalize_context_alloc --
7176b6d114aSBen Gras * Allocate an internalize context.
7186b6d114aSBen Gras */
7196b6d114aSBen Gras struct _prop_object_internalize_context *
_prop_object_internalize_context_alloc(const char * xml)7206b6d114aSBen Gras _prop_object_internalize_context_alloc(const char *xml)
7216b6d114aSBen Gras {
7226b6d114aSBen Gras struct _prop_object_internalize_context *ctx;
7236b6d114aSBen Gras
7246b6d114aSBen Gras ctx = _PROP_MALLOC(sizeof(struct _prop_object_internalize_context),
7256b6d114aSBen Gras M_TEMP);
7266b6d114aSBen Gras if (ctx == NULL)
7276b6d114aSBen Gras return (NULL);
7286b6d114aSBen Gras
7296b6d114aSBen Gras ctx->poic_xml = ctx->poic_cp = xml;
7306b6d114aSBen Gras
7316b6d114aSBen Gras /*
7326b6d114aSBen Gras * Skip any whitespace and XML preamble stuff that we don't
7336b6d114aSBen Gras * know about / care about.
7346b6d114aSBen Gras */
7356b6d114aSBen Gras for (;;) {
7366b6d114aSBen Gras while (_PROP_ISSPACE(*xml))
7376b6d114aSBen Gras xml++;
7386b6d114aSBen Gras if (_PROP_EOF(*xml) || *xml != '<')
7396b6d114aSBen Gras goto bad;
7406b6d114aSBen Gras
7416b6d114aSBen Gras #define MATCH(str) (memcmp(&xml[1], str, sizeof(str) - 1) == 0)
7426b6d114aSBen Gras
7436b6d114aSBen Gras /*
7446b6d114aSBen Gras * Skip over the XML preamble that Apple XML property
7456b6d114aSBen Gras * lists usually include at the top of the file.
7466b6d114aSBen Gras */
7476b6d114aSBen Gras if (MATCH("?xml ") ||
7486b6d114aSBen Gras MATCH("!DOCTYPE plist")) {
7496b6d114aSBen Gras while (*xml != '>' && !_PROP_EOF(*xml))
7506b6d114aSBen Gras xml++;
7516b6d114aSBen Gras if (_PROP_EOF(*xml))
7526b6d114aSBen Gras goto bad;
7536b6d114aSBen Gras xml++; /* advance past the '>' */
7546b6d114aSBen Gras continue;
7556b6d114aSBen Gras }
7566b6d114aSBen Gras
7576b6d114aSBen Gras if (MATCH("<!--")) {
7586b6d114aSBen Gras ctx->poic_cp = xml + 4;
7596b6d114aSBen Gras if (_prop_object_internalize_skip_comment(ctx) == false)
7606b6d114aSBen Gras goto bad;
7616b6d114aSBen Gras xml = ctx->poic_cp;
7626b6d114aSBen Gras continue;
7636b6d114aSBen Gras }
7646b6d114aSBen Gras
7656b6d114aSBen Gras #undef MATCH
7666b6d114aSBen Gras
7676b6d114aSBen Gras /*
7686b6d114aSBen Gras * We don't think we should skip it, so let's hope we can
7696b6d114aSBen Gras * parse it.
7706b6d114aSBen Gras */
7716b6d114aSBen Gras break;
7726b6d114aSBen Gras }
7736b6d114aSBen Gras
7746b6d114aSBen Gras ctx->poic_cp = xml;
7756b6d114aSBen Gras return (ctx);
7766b6d114aSBen Gras bad:
7776b6d114aSBen Gras _PROP_FREE(ctx, M_TEMP);
7786b6d114aSBen Gras return (NULL);
7796b6d114aSBen Gras }
7806b6d114aSBen Gras
7816b6d114aSBen Gras /*
7826b6d114aSBen Gras * _prop_object_internalize_context_free --
7836b6d114aSBen Gras * Free an internalize context.
7846b6d114aSBen Gras */
7856b6d114aSBen Gras void
_prop_object_internalize_context_free(struct _prop_object_internalize_context * ctx)7866b6d114aSBen Gras _prop_object_internalize_context_free(
7876b6d114aSBen Gras struct _prop_object_internalize_context *ctx)
7886b6d114aSBen Gras {
7896b6d114aSBen Gras
7906b6d114aSBen Gras _PROP_FREE(ctx, M_TEMP);
7916b6d114aSBen Gras }
7926b6d114aSBen Gras
7936b6d114aSBen Gras #if !defined(_KERNEL) && !defined(_STANDALONE)
7946b6d114aSBen Gras /*
7956b6d114aSBen Gras * _prop_object_externalize_file_dirname --
7966b6d114aSBen Gras * dirname(3), basically. We have to roll our own because the
7976b6d114aSBen Gras * system dirname(3) isn't reentrant.
7986b6d114aSBen Gras */
7996b6d114aSBen Gras static void
_prop_object_externalize_file_dirname(const char * path,char * result)8006b6d114aSBen Gras _prop_object_externalize_file_dirname(const char *path, char *result)
8016b6d114aSBen Gras {
8026b6d114aSBen Gras const char *lastp;
8036b6d114aSBen Gras size_t len;
8046b6d114aSBen Gras
8056b6d114aSBen Gras /*
8066b6d114aSBen Gras * If `path' is a NULL pointer or points to an empty string,
8076b6d114aSBen Gras * return ".".
8086b6d114aSBen Gras */
8096b6d114aSBen Gras if (path == NULL || *path == '\0')
8106b6d114aSBen Gras goto singledot;
8116b6d114aSBen Gras
8126b6d114aSBen Gras /* String trailing slashes, if any. */
8136b6d114aSBen Gras lastp = path + strlen(path) - 1;
8146b6d114aSBen Gras while (lastp != path && *lastp == '/')
8156b6d114aSBen Gras lastp--;
8166b6d114aSBen Gras
8176b6d114aSBen Gras /* Terminate path at the last occurrence of '/'. */
8186b6d114aSBen Gras do {
8196b6d114aSBen Gras if (*lastp == '/') {
8206b6d114aSBen Gras /* Strip trailing slashes, if any. */
8216b6d114aSBen Gras while (lastp != path && *lastp == '/')
8226b6d114aSBen Gras lastp--;
8236b6d114aSBen Gras
8246b6d114aSBen Gras /* ...and copy the result into the result buffer. */
8256b6d114aSBen Gras len = (lastp - path) + 1 /* last char */;
8266b6d114aSBen Gras if (len > (PATH_MAX - 1))
8276b6d114aSBen Gras len = PATH_MAX - 1;
8286b6d114aSBen Gras
8296b6d114aSBen Gras memcpy(result, path, len);
8306b6d114aSBen Gras result[len] = '\0';
8316b6d114aSBen Gras return;
8326b6d114aSBen Gras }
8336b6d114aSBen Gras } while (--lastp >= path);
8346b6d114aSBen Gras
8356b6d114aSBen Gras /* No /'s found, return ".". */
8366b6d114aSBen Gras singledot:
8376b6d114aSBen Gras strcpy(result, ".");
8386b6d114aSBen Gras }
8396b6d114aSBen Gras
8406b6d114aSBen Gras /*
8416b6d114aSBen Gras * _prop_object_externalize_write_file --
8426b6d114aSBen Gras * Write an externalized dictionary to the specified file.
8436b6d114aSBen Gras * The file is written atomically from the caller's perspective,
8446b6d114aSBen Gras * and the mode set to 0666 modified by the caller's umask.
8456b6d114aSBen Gras */
8466b6d114aSBen Gras bool
_prop_object_externalize_write_file(const char * fname,const char * xml,size_t len)8476b6d114aSBen Gras _prop_object_externalize_write_file(const char *fname, const char *xml,
8486b6d114aSBen Gras size_t len)
8496b6d114aSBen Gras {
8506b6d114aSBen Gras char tname[PATH_MAX];
8516b6d114aSBen Gras int fd;
8526b6d114aSBen Gras int save_errno;
8536b6d114aSBen Gras mode_t myumask;
8546b6d114aSBen Gras
8556b6d114aSBen Gras if (len > SSIZE_MAX) {
8566b6d114aSBen Gras errno = EFBIG;
8576b6d114aSBen Gras return (false);
8586b6d114aSBen Gras }
8596b6d114aSBen Gras
8606b6d114aSBen Gras /*
8616b6d114aSBen Gras * Get the directory name where the file is to be written
8626b6d114aSBen Gras * and create the temporary file.
8636b6d114aSBen Gras */
8646b6d114aSBen Gras _prop_object_externalize_file_dirname(fname, tname);
865f14fb602SLionel Sambuc #define PLISTTMP "/.plistXXXXXX"
866f14fb602SLionel Sambuc if (strlen(tname) + strlen(PLISTTMP) >= sizeof(tname)) {
8676b6d114aSBen Gras errno = ENAMETOOLONG;
8686b6d114aSBen Gras return (false);
8696b6d114aSBen Gras }
870f14fb602SLionel Sambuc strcat(tname, PLISTTMP);
871f14fb602SLionel Sambuc #undef PLISTTMP
872f14fb602SLionel Sambuc
8736b6d114aSBen Gras if ((fd = mkstemp(tname)) == -1)
8746b6d114aSBen Gras return (false);
8756b6d114aSBen Gras
8766b6d114aSBen Gras if (write(fd, xml, len) != (ssize_t)len)
8776b6d114aSBen Gras goto bad;
8786b6d114aSBen Gras
8796b6d114aSBen Gras if (fsync(fd) == -1)
8806b6d114aSBen Gras goto bad;
8816b6d114aSBen Gras
8826b6d114aSBen Gras myumask = umask(0);
8836b6d114aSBen Gras (void)umask(myumask);
8846b6d114aSBen Gras if (fchmod(fd, 0666 & ~myumask) == -1)
8856b6d114aSBen Gras goto bad;
8866b6d114aSBen Gras
8876b6d114aSBen Gras (void) close(fd);
8886b6d114aSBen Gras fd = -1;
8896b6d114aSBen Gras
8906b6d114aSBen Gras if (rename(tname, fname) == -1)
8916b6d114aSBen Gras goto bad;
8926b6d114aSBen Gras
8936b6d114aSBen Gras return (true);
8946b6d114aSBen Gras
8956b6d114aSBen Gras bad:
8966b6d114aSBen Gras save_errno = errno;
8976b6d114aSBen Gras if (fd != -1)
8986b6d114aSBen Gras (void) close(fd);
8996b6d114aSBen Gras (void) unlink(tname);
9006b6d114aSBen Gras errno = save_errno;
9016b6d114aSBen Gras return (false);
9026b6d114aSBen Gras }
9036b6d114aSBen Gras
9046b6d114aSBen Gras /*
9056b6d114aSBen Gras * _prop_object_internalize_map_file --
9066b6d114aSBen Gras * Map a file for the purpose of internalizing it.
9076b6d114aSBen Gras */
9086b6d114aSBen Gras struct _prop_object_internalize_mapped_file *
_prop_object_internalize_map_file(const char * fname)9096b6d114aSBen Gras _prop_object_internalize_map_file(const char *fname)
9106b6d114aSBen Gras {
9116b6d114aSBen Gras struct stat sb;
9126b6d114aSBen Gras struct _prop_object_internalize_mapped_file *mf;
9136b6d114aSBen Gras size_t pgsize = (size_t)sysconf(_SC_PAGESIZE);
9146b6d114aSBen Gras size_t pgmask = pgsize - 1;
9156b6d114aSBen Gras bool need_guard = false;
9166b6d114aSBen Gras int fd;
9176b6d114aSBen Gras
9186b6d114aSBen Gras mf = _PROP_MALLOC(sizeof(*mf), M_TEMP);
9196b6d114aSBen Gras if (mf == NULL)
9206b6d114aSBen Gras return (NULL);
9216b6d114aSBen Gras
9226b6d114aSBen Gras fd = open(fname, O_RDONLY, 0400);
9236b6d114aSBen Gras if (fd == -1) {
9246b6d114aSBen Gras _PROP_FREE(mf, M_TEMP);
9256b6d114aSBen Gras return (NULL);
9266b6d114aSBen Gras }
9276b6d114aSBen Gras
9286b6d114aSBen Gras if (fstat(fd, &sb) == -1) {
9296b6d114aSBen Gras (void) close(fd);
9306b6d114aSBen Gras _PROP_FREE(mf, M_TEMP);
9316b6d114aSBen Gras return (NULL);
9326b6d114aSBen Gras }
9336b6d114aSBen Gras mf->poimf_mapsize = ((size_t)sb.st_size + pgmask) & ~pgmask;
9346b6d114aSBen Gras if (mf->poimf_mapsize < (size_t)sb.st_size) {
9356b6d114aSBen Gras (void) close(fd);
9366b6d114aSBen Gras _PROP_FREE(mf, M_TEMP);
9376b6d114aSBen Gras return (NULL);
9386b6d114aSBen Gras }
9396b6d114aSBen Gras
9406b6d114aSBen Gras /*
9416b6d114aSBen Gras * If the file length is an integral number of pages, then we
9426b6d114aSBen Gras * need to map a guard page at the end in order to provide the
9436b6d114aSBen Gras * necessary NUL-termination of the buffer.
9446b6d114aSBen Gras */
9456b6d114aSBen Gras if ((sb.st_size & pgmask) == 0)
9466b6d114aSBen Gras need_guard = true;
9476b6d114aSBen Gras
9486b6d114aSBen Gras mf->poimf_xml = mmap(NULL, need_guard ? mf->poimf_mapsize + pgsize
9496b6d114aSBen Gras : mf->poimf_mapsize,
9506b6d114aSBen Gras PROT_READ, MAP_FILE|MAP_SHARED, fd, (off_t)0);
9516b6d114aSBen Gras (void) close(fd);
9526b6d114aSBen Gras if (mf->poimf_xml == MAP_FAILED) {
9536b6d114aSBen Gras _PROP_FREE(mf, M_TEMP);
9546b6d114aSBen Gras return (NULL);
9556b6d114aSBen Gras }
956*0a6a1f1dSLionel Sambuc #if !defined(__minix)
9576b6d114aSBen Gras (void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_SEQUENTIAL);
9586b6d114aSBen Gras
9596b6d114aSBen Gras if (need_guard) {
9606b6d114aSBen Gras if (mmap(mf->poimf_xml + mf->poimf_mapsize,
9616b6d114aSBen Gras pgsize, PROT_READ,
9626b6d114aSBen Gras MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1,
9636b6d114aSBen Gras (off_t)0) == MAP_FAILED) {
9646b6d114aSBen Gras (void) munmap(mf->poimf_xml, mf->poimf_mapsize);
9656b6d114aSBen Gras _PROP_FREE(mf, M_TEMP);
9666b6d114aSBen Gras return (NULL);
9676b6d114aSBen Gras }
9686b6d114aSBen Gras mf->poimf_mapsize += pgsize;
9696b6d114aSBen Gras }
970*0a6a1f1dSLionel Sambuc #endif /* !defined(__minix) */
9716b6d114aSBen Gras
9726b6d114aSBen Gras return (mf);
9736b6d114aSBen Gras }
9746b6d114aSBen Gras
9756b6d114aSBen Gras /*
9766b6d114aSBen Gras * _prop_object_internalize_unmap_file --
9776b6d114aSBen Gras * Unmap a file previously mapped for internalizing.
9786b6d114aSBen Gras */
9796b6d114aSBen Gras void
9804684ddb6SLionel Sambuc #if defined(__minix)
9814684ddb6SLionel Sambuc __dead
9824684ddb6SLionel Sambuc #endif /* defined(__minix) */
_prop_object_internalize_unmap_file(struct _prop_object_internalize_mapped_file * mf)9836b6d114aSBen Gras _prop_object_internalize_unmap_file(
9846b6d114aSBen Gras struct _prop_object_internalize_mapped_file *mf)
9856b6d114aSBen Gras {
9866b6d114aSBen Gras
987*0a6a1f1dSLionel Sambuc #if !defined(__minix)
9886b6d114aSBen Gras (void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_DONTNEED);
9896b6d114aSBen Gras (void) munmap(mf->poimf_xml, mf->poimf_mapsize);
9906b6d114aSBen Gras _PROP_FREE(mf, M_TEMP);
9916b6d114aSBen Gras #else
9923260d16fSLionel Sambuc abort();
993*0a6a1f1dSLionel Sambuc #endif /* !defined(__minix) */
9946b6d114aSBen Gras }
9956b6d114aSBen Gras #endif /* !_KERNEL && !_STANDALONE */
9966b6d114aSBen Gras
9976b6d114aSBen Gras /*
9986b6d114aSBen Gras * prop_object_retain --
9996b6d114aSBen Gras * Increment the reference count on an object.
10006b6d114aSBen Gras */
10016b6d114aSBen Gras void
prop_object_retain(prop_object_t obj)10026b6d114aSBen Gras prop_object_retain(prop_object_t obj)
10036b6d114aSBen Gras {
10046b6d114aSBen Gras struct _prop_object *po = obj;
100584d9c625SLionel Sambuc uint32_t ncnt __unused;
10066b6d114aSBen Gras
1007f14fb602SLionel Sambuc _PROP_ATOMIC_INC32_NV(&po->po_refcnt, ncnt);
10086b6d114aSBen Gras _PROP_ASSERT(ncnt != 0);
10096b6d114aSBen Gras }
10106b6d114aSBen Gras
10116b6d114aSBen Gras /*
10126b6d114aSBen Gras * prop_object_release_emergency
10136b6d114aSBen Gras * A direct free with prop_object_release failed.
10146b6d114aSBen Gras * Walk down the tree until a leaf is found and
10156b6d114aSBen Gras * free that. Do not recurse to avoid stack overflows.
10166b6d114aSBen Gras *
10176b6d114aSBen Gras * This is a slow edge condition, but necessary to
10186b6d114aSBen Gras * guarantee that an object can always be freed.
10196b6d114aSBen Gras */
10206b6d114aSBen Gras static void
prop_object_release_emergency(prop_object_t obj)10216b6d114aSBen Gras prop_object_release_emergency(prop_object_t obj)
10226b6d114aSBen Gras {
10236b6d114aSBen Gras struct _prop_object *po;
10246b6d114aSBen Gras void (*unlock)(void);
10256b6d114aSBen Gras prop_object_t parent = NULL;
10266b6d114aSBen Gras uint32_t ocnt;
10276b6d114aSBen Gras
10286b6d114aSBen Gras for (;;) {
10296b6d114aSBen Gras po = obj;
10306b6d114aSBen Gras _PROP_ASSERT(obj);
10316b6d114aSBen Gras
10326b6d114aSBen Gras if (po->po_type->pot_lock != NULL)
10336b6d114aSBen Gras po->po_type->pot_lock();
10346b6d114aSBen Gras
10356b6d114aSBen Gras /* Save pointerto unlock function */
10366b6d114aSBen Gras unlock = po->po_type->pot_unlock;
10376b6d114aSBen Gras
10386b6d114aSBen Gras /* Dance a bit to make sure we always get the non-racy ocnt */
1039f14fb602SLionel Sambuc _PROP_ATOMIC_DEC32_NV(&po->po_refcnt, ocnt);
10406b6d114aSBen Gras ocnt++;
10416b6d114aSBen Gras _PROP_ASSERT(ocnt != 0);
10426b6d114aSBen Gras
10436b6d114aSBen Gras if (ocnt != 1) {
10446b6d114aSBen Gras if (unlock != NULL)
10456b6d114aSBen Gras unlock();
10466b6d114aSBen Gras break;
10476b6d114aSBen Gras }
10486b6d114aSBen Gras
10496b6d114aSBen Gras _PROP_ASSERT(po->po_type);
10506b6d114aSBen Gras if ((po->po_type->pot_free)(NULL, &obj) ==
10516b6d114aSBen Gras _PROP_OBJECT_FREE_DONE) {
10526b6d114aSBen Gras if (unlock != NULL)
10536b6d114aSBen Gras unlock();
10546b6d114aSBen Gras break;
10556b6d114aSBen Gras }
10566b6d114aSBen Gras
10576b6d114aSBen Gras if (unlock != NULL)
10586b6d114aSBen Gras unlock();
10596b6d114aSBen Gras
10606b6d114aSBen Gras parent = po;
1061f14fb602SLionel Sambuc _PROP_ATOMIC_INC32(&po->po_refcnt);
10626b6d114aSBen Gras }
10636b6d114aSBen Gras _PROP_ASSERT(parent);
10646b6d114aSBen Gras /* One object was just freed. */
10656b6d114aSBen Gras po = parent;
10666b6d114aSBen Gras (*po->po_type->pot_emergency_free)(parent);
10676b6d114aSBen Gras }
10686b6d114aSBen Gras
10696b6d114aSBen Gras /*
10706b6d114aSBen Gras * prop_object_release --
10716b6d114aSBen Gras * Decrement the reference count on an object.
10726b6d114aSBen Gras *
10736b6d114aSBen Gras * Free the object if we are releasing the final
10746b6d114aSBen Gras * reference.
10756b6d114aSBen Gras */
10766b6d114aSBen Gras void
prop_object_release(prop_object_t obj)10776b6d114aSBen Gras prop_object_release(prop_object_t obj)
10786b6d114aSBen Gras {
10796b6d114aSBen Gras struct _prop_object *po;
10806b6d114aSBen Gras struct _prop_stack stack;
10816b6d114aSBen Gras void (*unlock)(void);
10826b6d114aSBen Gras int ret;
10836b6d114aSBen Gras uint32_t ocnt;
10846b6d114aSBen Gras
10856b6d114aSBen Gras _prop_stack_init(&stack);
10866b6d114aSBen Gras
10876b6d114aSBen Gras do {
10886b6d114aSBen Gras do {
10896b6d114aSBen Gras po = obj;
10906b6d114aSBen Gras _PROP_ASSERT(obj);
10916b6d114aSBen Gras
10926b6d114aSBen Gras if (po->po_type->pot_lock != NULL)
10936b6d114aSBen Gras po->po_type->pot_lock();
10946b6d114aSBen Gras
10956b6d114aSBen Gras /* Save pointer to object unlock function */
10966b6d114aSBen Gras unlock = po->po_type->pot_unlock;
10976b6d114aSBen Gras
1098f14fb602SLionel Sambuc _PROP_ATOMIC_DEC32_NV(&po->po_refcnt, ocnt);
10996b6d114aSBen Gras ocnt++;
11006b6d114aSBen Gras _PROP_ASSERT(ocnt != 0);
11016b6d114aSBen Gras
11026b6d114aSBen Gras if (ocnt != 1) {
11036b6d114aSBen Gras ret = 0;
11046b6d114aSBen Gras if (unlock != NULL)
11056b6d114aSBen Gras unlock();
11066b6d114aSBen Gras break;
11076b6d114aSBen Gras }
11086b6d114aSBen Gras
11096b6d114aSBen Gras ret = (po->po_type->pot_free)(&stack, &obj);
11106b6d114aSBen Gras
11116b6d114aSBen Gras if (unlock != NULL)
11126b6d114aSBen Gras unlock();
11136b6d114aSBen Gras
11146b6d114aSBen Gras if (ret == _PROP_OBJECT_FREE_DONE)
11156b6d114aSBen Gras break;
11166b6d114aSBen Gras
1117f14fb602SLionel Sambuc _PROP_ATOMIC_INC32(&po->po_refcnt);
11186b6d114aSBen Gras } while (ret == _PROP_OBJECT_FREE_RECURSE);
11196b6d114aSBen Gras if (ret == _PROP_OBJECT_FREE_FAILED)
11206b6d114aSBen Gras prop_object_release_emergency(obj);
11216b6d114aSBen Gras } while (_prop_stack_pop(&stack, &obj, NULL, NULL, NULL));
11226b6d114aSBen Gras }
11236b6d114aSBen Gras
11246b6d114aSBen Gras /*
11256b6d114aSBen Gras * prop_object_type --
11266b6d114aSBen Gras * Return the type of an object.
11276b6d114aSBen Gras */
11286b6d114aSBen Gras prop_type_t
prop_object_type(prop_object_t obj)11296b6d114aSBen Gras prop_object_type(prop_object_t obj)
11306b6d114aSBen Gras {
11316b6d114aSBen Gras struct _prop_object *po = obj;
11326b6d114aSBen Gras
11336b6d114aSBen Gras if (obj == NULL)
11346b6d114aSBen Gras return (PROP_TYPE_UNKNOWN);
11356b6d114aSBen Gras
11366b6d114aSBen Gras return (po->po_type->pot_type);
11376b6d114aSBen Gras }
11386b6d114aSBen Gras
11396b6d114aSBen Gras /*
11406b6d114aSBen Gras * prop_object_equals --
11416b6d114aSBen Gras * Returns true if thw two objects are equivalent.
11426b6d114aSBen Gras */
11436b6d114aSBen Gras bool
prop_object_equals(prop_object_t obj1,prop_object_t obj2)11446b6d114aSBen Gras prop_object_equals(prop_object_t obj1, prop_object_t obj2)
11456b6d114aSBen Gras {
11466b6d114aSBen Gras return (prop_object_equals_with_error(obj1, obj2, NULL));
11476b6d114aSBen Gras }
11486b6d114aSBen Gras
11496b6d114aSBen Gras bool
prop_object_equals_with_error(prop_object_t obj1,prop_object_t obj2,bool * error_flag)11506b6d114aSBen Gras prop_object_equals_with_error(prop_object_t obj1, prop_object_t obj2,
11516b6d114aSBen Gras bool *error_flag)
11526b6d114aSBen Gras {
11536b6d114aSBen Gras struct _prop_object *po1;
11546b6d114aSBen Gras struct _prop_object *po2;
11556b6d114aSBen Gras void *stored_pointer1, *stored_pointer2;
11566b6d114aSBen Gras prop_object_t next_obj1, next_obj2;
11576b6d114aSBen Gras struct _prop_stack stack;
11586b6d114aSBen Gras _prop_object_equals_rv_t ret;
11596b6d114aSBen Gras
11606b6d114aSBen Gras _prop_stack_init(&stack);
11616b6d114aSBen Gras if (error_flag)
11626b6d114aSBen Gras *error_flag = false;
11636b6d114aSBen Gras
11646b6d114aSBen Gras start_subtree:
11656b6d114aSBen Gras stored_pointer1 = NULL;
11666b6d114aSBen Gras stored_pointer2 = NULL;
11676b6d114aSBen Gras po1 = obj1;
11686b6d114aSBen Gras po2 = obj2;
11696b6d114aSBen Gras
11706b6d114aSBen Gras if (po1->po_type != po2->po_type)
11716b6d114aSBen Gras return (false);
11726b6d114aSBen Gras
11736b6d114aSBen Gras continue_subtree:
11746b6d114aSBen Gras ret = (*po1->po_type->pot_equals)(obj1, obj2,
11756b6d114aSBen Gras &stored_pointer1, &stored_pointer2,
11766b6d114aSBen Gras &next_obj1, &next_obj2);
11776b6d114aSBen Gras if (ret == _PROP_OBJECT_EQUALS_FALSE)
11786b6d114aSBen Gras goto finish;
11796b6d114aSBen Gras if (ret == _PROP_OBJECT_EQUALS_TRUE) {
11806b6d114aSBen Gras if (!_prop_stack_pop(&stack, &obj1, &obj2,
11816b6d114aSBen Gras &stored_pointer1, &stored_pointer2))
11826b6d114aSBen Gras return true;
11836b6d114aSBen Gras po1 = obj1;
11846b6d114aSBen Gras po2 = obj2;
11856b6d114aSBen Gras goto continue_subtree;
11866b6d114aSBen Gras }
11876b6d114aSBen Gras _PROP_ASSERT(ret == _PROP_OBJECT_EQUALS_RECURSE);
11886b6d114aSBen Gras
11896b6d114aSBen Gras if (!_prop_stack_push(&stack, obj1, obj2,
11906b6d114aSBen Gras stored_pointer1, stored_pointer2)) {
11916b6d114aSBen Gras if (error_flag)
11926b6d114aSBen Gras *error_flag = true;
11936b6d114aSBen Gras goto finish;
11946b6d114aSBen Gras }
11956b6d114aSBen Gras obj1 = next_obj1;
11966b6d114aSBen Gras obj2 = next_obj2;
11976b6d114aSBen Gras goto start_subtree;
11986b6d114aSBen Gras
11996b6d114aSBen Gras finish:
12006b6d114aSBen Gras while (_prop_stack_pop(&stack, &obj1, &obj2, NULL, NULL)) {
12016b6d114aSBen Gras po1 = obj1;
12026b6d114aSBen Gras (*po1->po_type->pot_equals_finish)(obj1, obj2);
12036b6d114aSBen Gras }
12046b6d114aSBen Gras return (false);
12056b6d114aSBen Gras }
12066b6d114aSBen Gras
12076b6d114aSBen Gras /*
12086b6d114aSBen Gras * prop_object_iterator_next --
12096b6d114aSBen Gras * Return the next item during an iteration.
12106b6d114aSBen Gras */
12116b6d114aSBen Gras prop_object_t
prop_object_iterator_next(prop_object_iterator_t pi)12126b6d114aSBen Gras prop_object_iterator_next(prop_object_iterator_t pi)
12136b6d114aSBen Gras {
12146b6d114aSBen Gras
12156b6d114aSBen Gras return ((*pi->pi_next_object)(pi));
12166b6d114aSBen Gras }
12176b6d114aSBen Gras
12186b6d114aSBen Gras /*
12196b6d114aSBen Gras * prop_object_iterator_reset --
12206b6d114aSBen Gras * Reset the iterator to the first object so as to restart
12216b6d114aSBen Gras * iteration.
12226b6d114aSBen Gras */
12236b6d114aSBen Gras void
prop_object_iterator_reset(prop_object_iterator_t pi)12246b6d114aSBen Gras prop_object_iterator_reset(prop_object_iterator_t pi)
12256b6d114aSBen Gras {
12266b6d114aSBen Gras
12276b6d114aSBen Gras (*pi->pi_reset)(pi);
12286b6d114aSBen Gras }
12296b6d114aSBen Gras
12306b6d114aSBen Gras /*
12316b6d114aSBen Gras * prop_object_iterator_release --
12326b6d114aSBen Gras * Release the object iterator.
12336b6d114aSBen Gras */
12346b6d114aSBen Gras void
prop_object_iterator_release(prop_object_iterator_t pi)12356b6d114aSBen Gras prop_object_iterator_release(prop_object_iterator_t pi)
12366b6d114aSBen Gras {
12376b6d114aSBen Gras
12386b6d114aSBen Gras prop_object_release(pi->pi_obj);
12396b6d114aSBen Gras _PROP_FREE(pi, M_TEMP);
12406b6d114aSBen Gras }
1241