1*ab821170Smartin /* $NetBSD: prop_object.c,v 1.11 2006/10/18 19:15:46 martin Exp $ */ 2774eb1a3Sthorpej 3774eb1a3Sthorpej /*- 4774eb1a3Sthorpej * Copyright (c) 2006 The NetBSD Foundation, Inc. 5774eb1a3Sthorpej * All rights reserved. 6774eb1a3Sthorpej * 7774eb1a3Sthorpej * This code is derived from software contributed to The NetBSD Foundation 8774eb1a3Sthorpej * by Jason R. Thorpe. 9774eb1a3Sthorpej * 10774eb1a3Sthorpej * Redistribution and use in source and binary forms, with or without 11774eb1a3Sthorpej * modification, are permitted provided that the following conditions 12774eb1a3Sthorpej * are met: 13774eb1a3Sthorpej * 1. Redistributions of source code must retain the above copyright 14774eb1a3Sthorpej * notice, this list of conditions and the following disclaimer. 15774eb1a3Sthorpej * 2. Redistributions in binary form must reproduce the above copyright 16774eb1a3Sthorpej * notice, this list of conditions and the following disclaimer in the 17774eb1a3Sthorpej * documentation and/or other materials provided with the distribution. 18774eb1a3Sthorpej * 3. All advertising materials mentioning features or use of this software 19774eb1a3Sthorpej * must display the following acknowledgement: 20774eb1a3Sthorpej * This product includes software developed by the NetBSD 21774eb1a3Sthorpej * Foundation, Inc. and its contributors. 22774eb1a3Sthorpej * 4. Neither the name of The NetBSD Foundation nor the names of its 23774eb1a3Sthorpej * contributors may be used to endorse or promote products derived 24774eb1a3Sthorpej * from this software without specific prior written permission. 25774eb1a3Sthorpej * 26774eb1a3Sthorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27774eb1a3Sthorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28774eb1a3Sthorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29774eb1a3Sthorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30774eb1a3Sthorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31774eb1a3Sthorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32774eb1a3Sthorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33774eb1a3Sthorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34774eb1a3Sthorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35774eb1a3Sthorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36774eb1a3Sthorpej * POSSIBILITY OF SUCH DAMAGE. 37774eb1a3Sthorpej */ 38774eb1a3Sthorpej 39774eb1a3Sthorpej #include <prop/prop_object.h> 40774eb1a3Sthorpej #include "prop_object_impl.h" 41774eb1a3Sthorpej 42d21620b2Sthorpej #if !defined(_KERNEL) && !defined(_STANDALONE) 43d21620b2Sthorpej #include <sys/mman.h> 44d21620b2Sthorpej #include <sys/stat.h> 45d21620b2Sthorpej #include <errno.h> 46d21620b2Sthorpej #include <fcntl.h> 47d21620b2Sthorpej #include <limits.h> 48d21620b2Sthorpej #include <unistd.h> 49d21620b2Sthorpej #endif 50d21620b2Sthorpej 51774eb1a3Sthorpej #ifdef _STANDALONE 52774eb1a3Sthorpej void * 53774eb1a3Sthorpej _prop_standalone_calloc(size_t size) 54774eb1a3Sthorpej { 55774eb1a3Sthorpej void *rv; 56774eb1a3Sthorpej 57774eb1a3Sthorpej rv = alloc(size); 58774eb1a3Sthorpej if (rv != NULL) 59774eb1a3Sthorpej memset(rv, 0, size); 60774eb1a3Sthorpej 61774eb1a3Sthorpej return (rv); 62774eb1a3Sthorpej } 633e69f1b2Sthorpej 643e69f1b2Sthorpej void * 653e69f1b2Sthorpej _prop_standalone_realloc(void *v, size_t size) 663e69f1b2Sthorpej { 673e69f1b2Sthorpej void *rv; 683e69f1b2Sthorpej 693e69f1b2Sthorpej rv = alloc(size); 703e69f1b2Sthorpej if (rv != NULL) { 713e69f1b2Sthorpej memcpy(rv, v, size); /* XXX */ 723e69f1b2Sthorpej dealloc(v, 0); /* XXX */ 733e69f1b2Sthorpej } 743e69f1b2Sthorpej 753e69f1b2Sthorpej return (rv); 763e69f1b2Sthorpej } 77774eb1a3Sthorpej #endif /* _STANDALONE */ 78774eb1a3Sthorpej 79774eb1a3Sthorpej /* 80774eb1a3Sthorpej * _prop_object_init -- 81774eb1a3Sthorpej * Initialize an object. Called when sub-classes create 82774eb1a3Sthorpej * an instance. 83774eb1a3Sthorpej */ 84774eb1a3Sthorpej void 853e69f1b2Sthorpej _prop_object_init(struct _prop_object *po, const struct _prop_object_type *pot) 86774eb1a3Sthorpej { 87774eb1a3Sthorpej 883e69f1b2Sthorpej po->po_type = pot; 89774eb1a3Sthorpej po->po_refcnt = 1; 90774eb1a3Sthorpej } 91774eb1a3Sthorpej 92774eb1a3Sthorpej /* 93774eb1a3Sthorpej * _prop_object_fini -- 94774eb1a3Sthorpej * Finalize an object. Called when sub-classes destroy 95774eb1a3Sthorpej * an instance. 96774eb1a3Sthorpej */ 97d21620b2Sthorpej /*ARGSUSED*/ 98774eb1a3Sthorpej void 99d9fd2cbcSthorpej _prop_object_fini(struct _prop_object *po _PROP_ARG_UNUSED) 100774eb1a3Sthorpej { 101774eb1a3Sthorpej /* Nothing to do, currently. */ 102774eb1a3Sthorpej } 103774eb1a3Sthorpej 104774eb1a3Sthorpej /* 105774eb1a3Sthorpej * _prop_object_externalize_start_tag -- 106774eb1a3Sthorpej * Append an XML-style start tag to the externalize buffer. 107774eb1a3Sthorpej */ 108774eb1a3Sthorpej boolean_t 109774eb1a3Sthorpej _prop_object_externalize_start_tag( 110774eb1a3Sthorpej struct _prop_object_externalize_context *ctx, const char *tag) 111774eb1a3Sthorpej { 112774eb1a3Sthorpej unsigned int i; 113774eb1a3Sthorpej 114774eb1a3Sthorpej for (i = 0; i < ctx->poec_depth; i++) { 115774eb1a3Sthorpej if (_prop_object_externalize_append_char(ctx, '\t') == FALSE) 116774eb1a3Sthorpej return (FALSE); 117774eb1a3Sthorpej } 118774eb1a3Sthorpej if (_prop_object_externalize_append_char(ctx, '<') == FALSE || 119774eb1a3Sthorpej _prop_object_externalize_append_cstring(ctx, tag) == FALSE || 120774eb1a3Sthorpej _prop_object_externalize_append_char(ctx, '>') == FALSE) 121774eb1a3Sthorpej return (FALSE); 122774eb1a3Sthorpej 123774eb1a3Sthorpej return (TRUE); 124774eb1a3Sthorpej } 125774eb1a3Sthorpej 126774eb1a3Sthorpej /* 127774eb1a3Sthorpej * _prop_object_externalize_end_tag -- 128774eb1a3Sthorpej * Append an XML-style end tag to the externalize buffer. 129774eb1a3Sthorpej */ 130774eb1a3Sthorpej boolean_t 131774eb1a3Sthorpej _prop_object_externalize_end_tag( 132774eb1a3Sthorpej struct _prop_object_externalize_context *ctx, const char *tag) 133774eb1a3Sthorpej { 134774eb1a3Sthorpej 135774eb1a3Sthorpej if (_prop_object_externalize_append_char(ctx, '<') == FALSE || 136774eb1a3Sthorpej _prop_object_externalize_append_char(ctx, '/') == FALSE || 137774eb1a3Sthorpej _prop_object_externalize_append_cstring(ctx, tag) == FALSE || 138774eb1a3Sthorpej _prop_object_externalize_append_char(ctx, '>') == FALSE || 139774eb1a3Sthorpej _prop_object_externalize_append_char(ctx, '\n') == FALSE) 140774eb1a3Sthorpej return (FALSE); 141774eb1a3Sthorpej 142774eb1a3Sthorpej return (TRUE); 143774eb1a3Sthorpej } 144774eb1a3Sthorpej 145774eb1a3Sthorpej /* 146774eb1a3Sthorpej * _prop_object_externalize_empty_tag -- 147774eb1a3Sthorpej * Append an XML-style empty tag to the externalize buffer. 148774eb1a3Sthorpej */ 149774eb1a3Sthorpej boolean_t 150774eb1a3Sthorpej _prop_object_externalize_empty_tag( 151774eb1a3Sthorpej struct _prop_object_externalize_context *ctx, const char *tag) 152774eb1a3Sthorpej { 153774eb1a3Sthorpej unsigned int i; 154774eb1a3Sthorpej 155774eb1a3Sthorpej for (i = 0; i < ctx->poec_depth; i++) { 156774eb1a3Sthorpej if (_prop_object_externalize_append_char(ctx, '\t') == FALSE) 157774eb1a3Sthorpej return (FALSE); 158774eb1a3Sthorpej } 159774eb1a3Sthorpej 160774eb1a3Sthorpej if (_prop_object_externalize_append_char(ctx, '<') == FALSE || 161774eb1a3Sthorpej _prop_object_externalize_append_cstring(ctx, tag) == FALSE || 162774eb1a3Sthorpej _prop_object_externalize_append_char(ctx, '/') == FALSE || 163774eb1a3Sthorpej _prop_object_externalize_append_char(ctx, '>') == FALSE || 164774eb1a3Sthorpej _prop_object_externalize_append_char(ctx, '\n') == FALSE) 165774eb1a3Sthorpej return (FALSE); 166774eb1a3Sthorpej 167774eb1a3Sthorpej return (TRUE); 168774eb1a3Sthorpej } 169774eb1a3Sthorpej 170774eb1a3Sthorpej /* 171774eb1a3Sthorpej * _prop_object_externalize_append_cstring -- 172774eb1a3Sthorpej * Append a C string to the externalize buffer. 173774eb1a3Sthorpej */ 174774eb1a3Sthorpej boolean_t 175774eb1a3Sthorpej _prop_object_externalize_append_cstring( 176774eb1a3Sthorpej struct _prop_object_externalize_context *ctx, const char *cp) 177774eb1a3Sthorpej { 178774eb1a3Sthorpej 179774eb1a3Sthorpej while (*cp != '\0') { 180774eb1a3Sthorpej if (_prop_object_externalize_append_char(ctx, 181774eb1a3Sthorpej (unsigned char) *cp) == FALSE) 182774eb1a3Sthorpej return (FALSE); 183774eb1a3Sthorpej cp++; 184774eb1a3Sthorpej } 185774eb1a3Sthorpej 186774eb1a3Sthorpej return (TRUE); 187774eb1a3Sthorpej } 188774eb1a3Sthorpej 189774eb1a3Sthorpej /* 190774eb1a3Sthorpej * _prop_object_externalize_append_encoded_cstring -- 191774eb1a3Sthorpej * Append an encoded C string to the externalize buffer. 192774eb1a3Sthorpej */ 193774eb1a3Sthorpej boolean_t 194774eb1a3Sthorpej _prop_object_externalize_append_encoded_cstring( 195774eb1a3Sthorpej struct _prop_object_externalize_context *ctx, const char *cp) 196774eb1a3Sthorpej { 197774eb1a3Sthorpej 198774eb1a3Sthorpej while (*cp != '\0') { 199774eb1a3Sthorpej switch (*cp) { 200774eb1a3Sthorpej case '<': 201774eb1a3Sthorpej if (_prop_object_externalize_append_cstring(ctx, 202774eb1a3Sthorpej "<") == FALSE) 203774eb1a3Sthorpej return (FALSE); 204774eb1a3Sthorpej break; 205774eb1a3Sthorpej case '>': 206774eb1a3Sthorpej if (_prop_object_externalize_append_cstring(ctx, 207774eb1a3Sthorpej ">") == FALSE) 208774eb1a3Sthorpej return (FALSE); 209774eb1a3Sthorpej break; 210774eb1a3Sthorpej case '&': 211774eb1a3Sthorpej if (_prop_object_externalize_append_cstring(ctx, 212774eb1a3Sthorpej "&") == FALSE) 213774eb1a3Sthorpej return (FALSE); 214774eb1a3Sthorpej break; 215774eb1a3Sthorpej default: 216774eb1a3Sthorpej if (_prop_object_externalize_append_char(ctx, 217774eb1a3Sthorpej (unsigned char) *cp) == FALSE) 218774eb1a3Sthorpej return (FALSE); 219774eb1a3Sthorpej break; 220774eb1a3Sthorpej } 221774eb1a3Sthorpej cp++; 222774eb1a3Sthorpej } 223774eb1a3Sthorpej 224774eb1a3Sthorpej return (TRUE); 225774eb1a3Sthorpej } 226774eb1a3Sthorpej 227*ab821170Smartin #define BUF_EXPAND 256 228774eb1a3Sthorpej 229774eb1a3Sthorpej /* 230774eb1a3Sthorpej * _prop_object_externalize_append_char -- 231774eb1a3Sthorpej * Append a single character to the externalize buffer. 232774eb1a3Sthorpej */ 233774eb1a3Sthorpej boolean_t 234774eb1a3Sthorpej _prop_object_externalize_append_char( 235774eb1a3Sthorpej struct _prop_object_externalize_context *ctx, unsigned char c) 236774eb1a3Sthorpej { 237774eb1a3Sthorpej 238774eb1a3Sthorpej _PROP_ASSERT(ctx->poec_capacity != 0); 239774eb1a3Sthorpej _PROP_ASSERT(ctx->poec_buf != NULL); 240774eb1a3Sthorpej _PROP_ASSERT(ctx->poec_len <= ctx->poec_capacity); 241774eb1a3Sthorpej 242774eb1a3Sthorpej if (ctx->poec_len == ctx->poec_capacity) { 2433e69f1b2Sthorpej char *cp = _PROP_REALLOC(ctx->poec_buf, 2443e69f1b2Sthorpej ctx->poec_capacity + BUF_EXPAND, 245774eb1a3Sthorpej M_TEMP); 246774eb1a3Sthorpej if (cp == NULL) 247774eb1a3Sthorpej return (FALSE); 248774eb1a3Sthorpej ctx->poec_capacity = ctx->poec_capacity + BUF_EXPAND; 249774eb1a3Sthorpej ctx->poec_buf = cp; 250774eb1a3Sthorpej } 251774eb1a3Sthorpej 252774eb1a3Sthorpej ctx->poec_buf[ctx->poec_len++] = c; 253774eb1a3Sthorpej 254774eb1a3Sthorpej return (TRUE); 255774eb1a3Sthorpej } 256774eb1a3Sthorpej 257774eb1a3Sthorpej /* 258d21620b2Sthorpej * _prop_object_externalize_header -- 259d21620b2Sthorpej * Append the standard XML header to the externalize buffer. 260d21620b2Sthorpej */ 261d21620b2Sthorpej boolean_t 262d21620b2Sthorpej _prop_object_externalize_header(struct _prop_object_externalize_context *ctx) 263d21620b2Sthorpej { 264d21620b2Sthorpej static const char _plist_xml_header[] = 265d21620b2Sthorpej "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 266d21620b2Sthorpej "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"; 267d21620b2Sthorpej 268d21620b2Sthorpej if (_prop_object_externalize_append_cstring(ctx, 269d21620b2Sthorpej _plist_xml_header) == FALSE || 270d21620b2Sthorpej _prop_object_externalize_start_tag(ctx, 271d21620b2Sthorpej "plist version=\"1.0\"") == FALSE || 272d21620b2Sthorpej _prop_object_externalize_append_char(ctx, '\n') == FALSE) 273d21620b2Sthorpej return (FALSE); 274d21620b2Sthorpej 275d21620b2Sthorpej return (TRUE); 276d21620b2Sthorpej } 277d21620b2Sthorpej 278d21620b2Sthorpej /* 279d21620b2Sthorpej * _prop_object_externalize_footer -- 280d21620b2Sthorpej * Append the standard XML footer to the externalize buffer. This 281d21620b2Sthorpej * also NUL-terminates the buffer. 282d21620b2Sthorpej */ 283d21620b2Sthorpej boolean_t 284d21620b2Sthorpej _prop_object_externalize_footer(struct _prop_object_externalize_context *ctx) 285d21620b2Sthorpej { 286d21620b2Sthorpej 287d21620b2Sthorpej if (_prop_object_externalize_end_tag(ctx, "plist") == FALSE || 288d21620b2Sthorpej _prop_object_externalize_append_char(ctx, '\0') == FALSE) 289d21620b2Sthorpej return (FALSE); 290d21620b2Sthorpej 291d21620b2Sthorpej return (TRUE); 292d21620b2Sthorpej } 293d21620b2Sthorpej 294d21620b2Sthorpej /* 295774eb1a3Sthorpej * _prop_object_externalize_context_alloc -- 296774eb1a3Sthorpej * Allocate an externalize context. 297774eb1a3Sthorpej */ 298774eb1a3Sthorpej struct _prop_object_externalize_context * 299774eb1a3Sthorpej _prop_object_externalize_context_alloc(void) 300774eb1a3Sthorpej { 301774eb1a3Sthorpej struct _prop_object_externalize_context *ctx; 302774eb1a3Sthorpej 303774eb1a3Sthorpej ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP); 304774eb1a3Sthorpej if (ctx != NULL) { 305774eb1a3Sthorpej ctx->poec_buf = _PROP_MALLOC(BUF_EXPAND, M_TEMP); 306774eb1a3Sthorpej if (ctx->poec_buf == NULL) { 307774eb1a3Sthorpej _PROP_FREE(ctx, M_TEMP); 308774eb1a3Sthorpej return (NULL); 309774eb1a3Sthorpej } 310774eb1a3Sthorpej ctx->poec_len = 0; 311774eb1a3Sthorpej ctx->poec_capacity = BUF_EXPAND; 312774eb1a3Sthorpej ctx->poec_depth = 0; 313774eb1a3Sthorpej } 314774eb1a3Sthorpej return (ctx); 315774eb1a3Sthorpej } 316774eb1a3Sthorpej 317774eb1a3Sthorpej /* 318774eb1a3Sthorpej * _prop_object_externalize_context_free -- 319774eb1a3Sthorpej * Free an externalize context. 320774eb1a3Sthorpej */ 321774eb1a3Sthorpej void 322774eb1a3Sthorpej _prop_object_externalize_context_free( 323774eb1a3Sthorpej struct _prop_object_externalize_context *ctx) 324774eb1a3Sthorpej { 325774eb1a3Sthorpej 326774eb1a3Sthorpej /* Buffer is always freed by the caller. */ 327774eb1a3Sthorpej _PROP_FREE(ctx, M_TEMP); 328774eb1a3Sthorpej } 329774eb1a3Sthorpej 330774eb1a3Sthorpej /* 331774eb1a3Sthorpej * _prop_object_internalize_skip_comment -- 332774eb1a3Sthorpej * Skip the body and end tag of a comment. 333774eb1a3Sthorpej */ 334774eb1a3Sthorpej static boolean_t 335774eb1a3Sthorpej _prop_object_internalize_skip_comment( 336774eb1a3Sthorpej struct _prop_object_internalize_context *ctx) 337774eb1a3Sthorpej { 338774eb1a3Sthorpej const char *cp = ctx->poic_cp; 339774eb1a3Sthorpej 340774eb1a3Sthorpej while (!_PROP_EOF(*cp)) { 341774eb1a3Sthorpej if (cp[0] == '-' && 342774eb1a3Sthorpej cp[1] == '-' && 343774eb1a3Sthorpej cp[2] == '>') { 344774eb1a3Sthorpej ctx->poic_cp = cp + 3; 345774eb1a3Sthorpej return (TRUE); 346774eb1a3Sthorpej } 347774eb1a3Sthorpej cp++; 348774eb1a3Sthorpej } 349774eb1a3Sthorpej 350774eb1a3Sthorpej return (FALSE); /* ran out of buffer */ 351774eb1a3Sthorpej } 352774eb1a3Sthorpej 353774eb1a3Sthorpej /* 354774eb1a3Sthorpej * _prop_object_internalize_find_tag -- 355774eb1a3Sthorpej * Find the next tag in an XML stream. Optionally compare the found 356774eb1a3Sthorpej * tag to an expected tag name. State of the context is undefined 357774eb1a3Sthorpej * if this routine returns FALSE. Upon success, the context points 358774eb1a3Sthorpej * to the first octet after the tag. 359774eb1a3Sthorpej */ 360774eb1a3Sthorpej boolean_t 361774eb1a3Sthorpej _prop_object_internalize_find_tag(struct _prop_object_internalize_context *ctx, 362774eb1a3Sthorpej const char *tag, _prop_tag_type_t type) 363774eb1a3Sthorpej { 364774eb1a3Sthorpej const char *cp; 365774eb1a3Sthorpej size_t taglen; 366774eb1a3Sthorpej 367774eb1a3Sthorpej if (tag != NULL) 368774eb1a3Sthorpej taglen = strlen(tag); 369774eb1a3Sthorpej else 370774eb1a3Sthorpej taglen = 0; 371774eb1a3Sthorpej 372774eb1a3Sthorpej start_over: 373774eb1a3Sthorpej cp = ctx->poic_cp; 374774eb1a3Sthorpej 375774eb1a3Sthorpej /* 376774eb1a3Sthorpej * Find the start of the tag. 377774eb1a3Sthorpej */ 378774eb1a3Sthorpej while (_PROP_ISSPACE(*cp)) 379774eb1a3Sthorpej cp++; 380774eb1a3Sthorpej if (_PROP_EOF(*cp)) 381774eb1a3Sthorpej return (FALSE); 382774eb1a3Sthorpej 383774eb1a3Sthorpej if (*cp != '<') 384774eb1a3Sthorpej return (FALSE); 385774eb1a3Sthorpej 386774eb1a3Sthorpej ctx->poic_tag_start = cp++; 387774eb1a3Sthorpej if (_PROP_EOF(*cp)) 388774eb1a3Sthorpej return (FALSE); 389774eb1a3Sthorpej 390774eb1a3Sthorpej if (*cp == '!') { 391774eb1a3Sthorpej if (cp[1] != '-' || cp[2] != '-') 392774eb1a3Sthorpej return (FALSE); 393774eb1a3Sthorpej /* 394774eb1a3Sthorpej * Comment block -- only allowed if we are allowed to 395774eb1a3Sthorpej * return a start tag. 396774eb1a3Sthorpej */ 397774eb1a3Sthorpej if (type == _PROP_TAG_TYPE_END) 398774eb1a3Sthorpej return (FALSE); 399774eb1a3Sthorpej ctx->poic_cp = cp + 3; 400774eb1a3Sthorpej if (_prop_object_internalize_skip_comment(ctx) == FALSE) 401774eb1a3Sthorpej return (FALSE); 402774eb1a3Sthorpej goto start_over; 403774eb1a3Sthorpej } 404774eb1a3Sthorpej 405774eb1a3Sthorpej if (*cp == '/') { 406774eb1a3Sthorpej if (type != _PROP_TAG_TYPE_END && 407774eb1a3Sthorpej type != _PROP_TAG_TYPE_EITHER) 408774eb1a3Sthorpej return (FALSE); 409774eb1a3Sthorpej cp++; 410774eb1a3Sthorpej if (_PROP_EOF(*cp)) 411774eb1a3Sthorpej return (FALSE); 412774eb1a3Sthorpej ctx->poic_tag_type = _PROP_TAG_TYPE_END; 413774eb1a3Sthorpej } else { 414774eb1a3Sthorpej if (type != _PROP_TAG_TYPE_START && 415774eb1a3Sthorpej type != _PROP_TAG_TYPE_EITHER) 416774eb1a3Sthorpej return (FALSE); 417774eb1a3Sthorpej ctx->poic_tag_type = _PROP_TAG_TYPE_START; 418774eb1a3Sthorpej } 419774eb1a3Sthorpej 420774eb1a3Sthorpej ctx->poic_tagname = cp; 421774eb1a3Sthorpej 422774eb1a3Sthorpej while (!_PROP_ISSPACE(*cp) && *cp != '/' && *cp != '>') 423774eb1a3Sthorpej cp++; 424774eb1a3Sthorpej if (_PROP_EOF(*cp)) 425774eb1a3Sthorpej return (FALSE); 426774eb1a3Sthorpej 427774eb1a3Sthorpej ctx->poic_tagname_len = cp - ctx->poic_tagname; 428774eb1a3Sthorpej 429774eb1a3Sthorpej /* Make sure this is the tag we're looking for. */ 430774eb1a3Sthorpej if (tag != NULL && 431774eb1a3Sthorpej (taglen != ctx->poic_tagname_len || 432774eb1a3Sthorpej memcmp(tag, ctx->poic_tagname, taglen) != 0)) 433774eb1a3Sthorpej return (FALSE); 434774eb1a3Sthorpej 435774eb1a3Sthorpej /* Check for empty tag. */ 436774eb1a3Sthorpej if (*cp == '/') { 437774eb1a3Sthorpej if (ctx->poic_tag_type != _PROP_TAG_TYPE_START) 438774eb1a3Sthorpej return(FALSE); /* only valid on start tags */ 439774eb1a3Sthorpej ctx->poic_is_empty_element = TRUE; 440774eb1a3Sthorpej cp++; 441774eb1a3Sthorpej if (_PROP_EOF(*cp) || *cp != '>') 442774eb1a3Sthorpej return (FALSE); 443774eb1a3Sthorpej } else 444774eb1a3Sthorpej ctx->poic_is_empty_element = FALSE; 445774eb1a3Sthorpej 446774eb1a3Sthorpej /* Easy case of no arguments. */ 447774eb1a3Sthorpej if (*cp == '>') { 448774eb1a3Sthorpej ctx->poic_tagattr = NULL; 449774eb1a3Sthorpej ctx->poic_tagattr_len = 0; 450774eb1a3Sthorpej ctx->poic_tagattrval = NULL; 451774eb1a3Sthorpej ctx->poic_tagattrval_len = 0; 452774eb1a3Sthorpej ctx->poic_cp = cp + 1; 453774eb1a3Sthorpej return (TRUE); 454774eb1a3Sthorpej } 455774eb1a3Sthorpej 456774eb1a3Sthorpej _PROP_ASSERT(!_PROP_EOF(*cp)); 457774eb1a3Sthorpej cp++; 458774eb1a3Sthorpej if (_PROP_EOF(*cp)) 459774eb1a3Sthorpej return (FALSE); 460774eb1a3Sthorpej 461774eb1a3Sthorpej while (_PROP_ISSPACE(*cp)) 462774eb1a3Sthorpej cp++; 463774eb1a3Sthorpej if (_PROP_EOF(*cp)) 464774eb1a3Sthorpej return (FALSE); 465774eb1a3Sthorpej 466774eb1a3Sthorpej ctx->poic_tagattr = cp; 467774eb1a3Sthorpej 468774eb1a3Sthorpej while (!_PROP_ISSPACE(*cp) && *cp != '=') 469774eb1a3Sthorpej cp++; 470774eb1a3Sthorpej if (_PROP_EOF(*cp)) 471774eb1a3Sthorpej return (FALSE); 472774eb1a3Sthorpej 473774eb1a3Sthorpej ctx->poic_tagattr_len = cp - ctx->poic_tagattr; 474774eb1a3Sthorpej 475774eb1a3Sthorpej cp++; 476774eb1a3Sthorpej if (*cp != '\"') 477774eb1a3Sthorpej return (FALSE); 478774eb1a3Sthorpej cp++; 479774eb1a3Sthorpej if (_PROP_EOF(*cp)) 480774eb1a3Sthorpej return (FALSE); 481774eb1a3Sthorpej 482774eb1a3Sthorpej ctx->poic_tagattrval = cp; 483774eb1a3Sthorpej while (*cp != '\"') 484774eb1a3Sthorpej cp++; 485774eb1a3Sthorpej if (_PROP_EOF(*cp)) 486774eb1a3Sthorpej return (FALSE); 487774eb1a3Sthorpej ctx->poic_tagattrval_len = cp - ctx->poic_tagattrval; 488774eb1a3Sthorpej 489774eb1a3Sthorpej cp++; 490774eb1a3Sthorpej if (*cp != '>') 491774eb1a3Sthorpej return (FALSE); 492774eb1a3Sthorpej 493774eb1a3Sthorpej ctx->poic_cp = cp + 1; 494774eb1a3Sthorpej return (TRUE); 495774eb1a3Sthorpej } 496774eb1a3Sthorpej 497774eb1a3Sthorpej /* 498774eb1a3Sthorpej * _prop_object_internalize_decode_string -- 499774eb1a3Sthorpej * Decode an encoded string. 500774eb1a3Sthorpej */ 501774eb1a3Sthorpej boolean_t 502774eb1a3Sthorpej _prop_object_internalize_decode_string( 503774eb1a3Sthorpej struct _prop_object_internalize_context *ctx, 504774eb1a3Sthorpej char *target, size_t targsize, size_t *sizep, 505774eb1a3Sthorpej const char **cpp) 506774eb1a3Sthorpej { 507774eb1a3Sthorpej const char *src; 508774eb1a3Sthorpej size_t tarindex; 509774eb1a3Sthorpej char c; 510774eb1a3Sthorpej 511774eb1a3Sthorpej tarindex = 0; 512774eb1a3Sthorpej src = ctx->poic_cp; 513774eb1a3Sthorpej 514774eb1a3Sthorpej for (;;) { 515774eb1a3Sthorpej if (_PROP_EOF(*src)) 516774eb1a3Sthorpej return (FALSE); 517774eb1a3Sthorpej if (*src == '<') { 518774eb1a3Sthorpej break; 519774eb1a3Sthorpej } 520774eb1a3Sthorpej 521774eb1a3Sthorpej if ((c = *src) == '&') { 522774eb1a3Sthorpej if (src[1] == 'a' && 523774eb1a3Sthorpej src[2] == 'm' && 524774eb1a3Sthorpej src[3] == 'p' && 525774eb1a3Sthorpej src[4] == ';') { 526774eb1a3Sthorpej c = '&'; 527774eb1a3Sthorpej src += 5; 528774eb1a3Sthorpej } else if (src[1] == 'l' && 529774eb1a3Sthorpej src[2] == 't' && 530774eb1a3Sthorpej src[3] == ';') { 531774eb1a3Sthorpej c = '<'; 532774eb1a3Sthorpej src += 4; 533774eb1a3Sthorpej } else if (src[1] == 'g' && 534774eb1a3Sthorpej src[2] == 't' && 535774eb1a3Sthorpej src[3] == ';') { 536774eb1a3Sthorpej c = '>'; 537774eb1a3Sthorpej src += 4; 538774eb1a3Sthorpej } else if (src[1] == 'a' && 539774eb1a3Sthorpej src[2] == 'p' && 540774eb1a3Sthorpej src[3] == 'o' && 541774eb1a3Sthorpej src[4] == 's' && 542774eb1a3Sthorpej src[5] == ';') { 543774eb1a3Sthorpej c = '\''; 544774eb1a3Sthorpej src += 6; 545774eb1a3Sthorpej } else if (src[1] == 'q' && 546774eb1a3Sthorpej src[2] == 'u' && 547774eb1a3Sthorpej src[3] == 'o' && 548774eb1a3Sthorpej src[4] == 't' && 549774eb1a3Sthorpej src[5] == ';') { 550774eb1a3Sthorpej c = '\"'; 551774eb1a3Sthorpej src += 6; 552774eb1a3Sthorpej } else 553774eb1a3Sthorpej return (FALSE); 554774eb1a3Sthorpej } else 555774eb1a3Sthorpej src++; 556774eb1a3Sthorpej if (target) { 557774eb1a3Sthorpej if (tarindex >= targsize) 558774eb1a3Sthorpej return (FALSE); 559774eb1a3Sthorpej target[tarindex] = c; 560774eb1a3Sthorpej } 561774eb1a3Sthorpej tarindex++; 562774eb1a3Sthorpej } 563774eb1a3Sthorpej 564774eb1a3Sthorpej _PROP_ASSERT(*src == '<'); 565774eb1a3Sthorpej if (sizep != NULL) 566774eb1a3Sthorpej *sizep = tarindex; 567774eb1a3Sthorpej if (cpp != NULL) 568774eb1a3Sthorpej *cpp = src; 569774eb1a3Sthorpej 570774eb1a3Sthorpej return (TRUE); 571774eb1a3Sthorpej } 572774eb1a3Sthorpej 573774eb1a3Sthorpej /* 574774eb1a3Sthorpej * _prop_object_internalize_match -- 575774eb1a3Sthorpej * Returns true if the two character streams match. 576774eb1a3Sthorpej */ 577774eb1a3Sthorpej boolean_t 578774eb1a3Sthorpej _prop_object_internalize_match(const char *str1, size_t len1, 579774eb1a3Sthorpej const char *str2, size_t len2) 580774eb1a3Sthorpej { 581774eb1a3Sthorpej 582774eb1a3Sthorpej return (len1 == len2 && memcmp(str1, str2, len1) == 0); 583774eb1a3Sthorpej } 584774eb1a3Sthorpej 585774eb1a3Sthorpej #define INTERNALIZER(t, f) \ 586774eb1a3Sthorpej { t, sizeof(t) - 1, f } 587774eb1a3Sthorpej 588774eb1a3Sthorpej static const struct _prop_object_internalizer { 589774eb1a3Sthorpej const char *poi_tag; 590774eb1a3Sthorpej size_t poi_taglen; 591774eb1a3Sthorpej prop_object_t (*poi_intern)( 592774eb1a3Sthorpej struct _prop_object_internalize_context *); 593774eb1a3Sthorpej } _prop_object_internalizer_table[] = { 594774eb1a3Sthorpej INTERNALIZER("array", _prop_array_internalize), 595774eb1a3Sthorpej 596774eb1a3Sthorpej INTERNALIZER("true", _prop_bool_internalize), 597774eb1a3Sthorpej INTERNALIZER("false", _prop_bool_internalize), 598774eb1a3Sthorpej 599774eb1a3Sthorpej INTERNALIZER("data", _prop_data_internalize), 600774eb1a3Sthorpej 601774eb1a3Sthorpej INTERNALIZER("dict", _prop_dictionary_internalize), 602774eb1a3Sthorpej 603774eb1a3Sthorpej INTERNALIZER("integer", _prop_number_internalize), 604774eb1a3Sthorpej 605774eb1a3Sthorpej INTERNALIZER("string", _prop_string_internalize), 606774eb1a3Sthorpej 6070616c072Schristos { 0, 0, NULL } 608774eb1a3Sthorpej }; 609774eb1a3Sthorpej 610774eb1a3Sthorpej #undef INTERNALIZER 611774eb1a3Sthorpej 612774eb1a3Sthorpej /* 613774eb1a3Sthorpej * _prop_object_internalize_by_tag -- 614774eb1a3Sthorpej * Determine the object type from the tag in the context and 615774eb1a3Sthorpej * internalize it. 616774eb1a3Sthorpej */ 617774eb1a3Sthorpej prop_object_t 618774eb1a3Sthorpej _prop_object_internalize_by_tag(struct _prop_object_internalize_context *ctx) 619774eb1a3Sthorpej { 620774eb1a3Sthorpej const struct _prop_object_internalizer *poi; 621774eb1a3Sthorpej 622774eb1a3Sthorpej for (poi = _prop_object_internalizer_table; 623774eb1a3Sthorpej poi->poi_tag != NULL; poi++) { 624774eb1a3Sthorpej if (_prop_object_internalize_match(ctx->poic_tagname, 625774eb1a3Sthorpej ctx->poic_tagname_len, 626774eb1a3Sthorpej poi->poi_tag, 627774eb1a3Sthorpej poi->poi_taglen)) 628774eb1a3Sthorpej return ((*poi->poi_intern)(ctx)); 629774eb1a3Sthorpej } 630774eb1a3Sthorpej 631774eb1a3Sthorpej return (NULL); 632774eb1a3Sthorpej } 633774eb1a3Sthorpej 634774eb1a3Sthorpej /* 635774eb1a3Sthorpej * _prop_object_internalize_context_alloc -- 636774eb1a3Sthorpej * Allocate an internalize context. 637774eb1a3Sthorpej */ 638774eb1a3Sthorpej struct _prop_object_internalize_context * 639774eb1a3Sthorpej _prop_object_internalize_context_alloc(const char *xml) 640774eb1a3Sthorpej { 641774eb1a3Sthorpej struct _prop_object_internalize_context *ctx; 642774eb1a3Sthorpej 643774eb1a3Sthorpej ctx = _PROP_MALLOC(sizeof(struct _prop_object_internalize_context), 644774eb1a3Sthorpej M_TEMP); 645774eb1a3Sthorpej if (ctx == NULL) 646774eb1a3Sthorpej return (NULL); 647774eb1a3Sthorpej 648774eb1a3Sthorpej ctx->poic_xml = ctx->poic_cp = xml; 649774eb1a3Sthorpej 650774eb1a3Sthorpej /* 651774eb1a3Sthorpej * Skip any whitespace and XML preamble stuff that we don't 652774eb1a3Sthorpej * know about / care about. 653774eb1a3Sthorpej */ 654774eb1a3Sthorpej for (;;) { 655774eb1a3Sthorpej while (_PROP_ISSPACE(*xml)) 656774eb1a3Sthorpej xml++; 657774eb1a3Sthorpej if (_PROP_EOF(*xml) || *xml != '<') 658774eb1a3Sthorpej goto bad; 659774eb1a3Sthorpej 660774eb1a3Sthorpej #define MATCH(str) (memcmp(&xml[1], str, sizeof(str) - 1) == 0) 661774eb1a3Sthorpej 662774eb1a3Sthorpej /* 663774eb1a3Sthorpej * Skip over the XML preamble that Apple XML property 664774eb1a3Sthorpej * lists usually include at the top of the file. 665774eb1a3Sthorpej */ 666774eb1a3Sthorpej if (MATCH("?xml ") || 667774eb1a3Sthorpej MATCH("!DOCTYPE plist")) { 668774eb1a3Sthorpej while (*xml != '>' && !_PROP_EOF(*xml)) 669774eb1a3Sthorpej xml++; 670774eb1a3Sthorpej if (_PROP_EOF(*xml)) 671774eb1a3Sthorpej goto bad; 672774eb1a3Sthorpej xml++; /* advance past the '>' */ 673774eb1a3Sthorpej continue; 674774eb1a3Sthorpej } 675774eb1a3Sthorpej 676774eb1a3Sthorpej if (MATCH("<!--")) { 677774eb1a3Sthorpej ctx->poic_cp = xml + 4; 678774eb1a3Sthorpej if (_prop_object_internalize_skip_comment(ctx) == FALSE) 679774eb1a3Sthorpej goto bad; 680774eb1a3Sthorpej xml = ctx->poic_cp; 681774eb1a3Sthorpej continue; 682774eb1a3Sthorpej } 683774eb1a3Sthorpej 684774eb1a3Sthorpej #undef MATCH 685774eb1a3Sthorpej 686774eb1a3Sthorpej /* 687774eb1a3Sthorpej * We don't think we should skip it, so let's hope we can 688774eb1a3Sthorpej * parse it. 689774eb1a3Sthorpej */ 690774eb1a3Sthorpej break; 691774eb1a3Sthorpej } 692774eb1a3Sthorpej 693774eb1a3Sthorpej ctx->poic_cp = xml; 694774eb1a3Sthorpej return (ctx); 695774eb1a3Sthorpej bad: 696774eb1a3Sthorpej _PROP_FREE(ctx, M_TEMP); 697774eb1a3Sthorpej return (NULL); 698774eb1a3Sthorpej } 699774eb1a3Sthorpej 700774eb1a3Sthorpej /* 701774eb1a3Sthorpej * _prop_object_internalize_context_free -- 702774eb1a3Sthorpej * Free an internalize context. 703774eb1a3Sthorpej */ 704774eb1a3Sthorpej void 705774eb1a3Sthorpej _prop_object_internalize_context_free( 706774eb1a3Sthorpej struct _prop_object_internalize_context *ctx) 707774eb1a3Sthorpej { 708774eb1a3Sthorpej 709774eb1a3Sthorpej _PROP_FREE(ctx, M_TEMP); 710774eb1a3Sthorpej } 711774eb1a3Sthorpej 712d21620b2Sthorpej #if !defined(_KERNEL) && !defined(_STANDALONE) 713d21620b2Sthorpej /* 714d21620b2Sthorpej * _prop_object_externalize_file_dirname -- 715d21620b2Sthorpej * dirname(3), basically. We have to roll our own because the 716d21620b2Sthorpej * system dirname(3) isn't reentrant. 717d21620b2Sthorpej */ 718d21620b2Sthorpej static void 719d21620b2Sthorpej _prop_object_externalize_file_dirname(const char *path, char *result) 720d21620b2Sthorpej { 721d21620b2Sthorpej const char *lastp; 722d21620b2Sthorpej size_t len; 723d21620b2Sthorpej 724d21620b2Sthorpej /* 725d21620b2Sthorpej * If `path' is a NULL pointer or points to an empty string, 726d21620b2Sthorpej * return ".". 727d21620b2Sthorpej */ 728d21620b2Sthorpej if (path == NULL || *path == '\0') 729d21620b2Sthorpej goto singledot; 730d21620b2Sthorpej 731d21620b2Sthorpej /* String trailing slashes, if any. */ 732d21620b2Sthorpej lastp = path + strlen(path) - 1; 733d21620b2Sthorpej while (lastp != path && *lastp == '/') 734d21620b2Sthorpej lastp--; 735d21620b2Sthorpej 736d21620b2Sthorpej /* Terminate path at the last occurrence of '/'. */ 737d21620b2Sthorpej do { 738d21620b2Sthorpej if (*lastp == '/') { 739d21620b2Sthorpej /* Strip trailing slashes, if any. */ 740d21620b2Sthorpej while (lastp != path && *lastp == '/') 741d21620b2Sthorpej lastp--; 742d21620b2Sthorpej 743d21620b2Sthorpej /* ...and copy the result into the result buffer. */ 744d21620b2Sthorpej len = (lastp - path) + 1 /* last char */; 745d21620b2Sthorpej if (len > (PATH_MAX - 1)) 746d21620b2Sthorpej len = PATH_MAX - 1; 747d21620b2Sthorpej 748d21620b2Sthorpej memcpy(result, path, len); 749d21620b2Sthorpej result[len] = '\0'; 750d21620b2Sthorpej return; 751d21620b2Sthorpej } 752d21620b2Sthorpej } while (--lastp >= path); 753d21620b2Sthorpej 754d21620b2Sthorpej /* No /'s found, return ".". */ 755d21620b2Sthorpej singledot: 756d21620b2Sthorpej strcpy(result, "."); 757d21620b2Sthorpej } 758d21620b2Sthorpej 759d21620b2Sthorpej /* 760d21620b2Sthorpej * _prop_object_externalize_write_file -- 761d21620b2Sthorpej * Write an externalized dictionary to the specified file. 762d21620b2Sthorpej * The file is written atomically from the caller's perspective, 763d21620b2Sthorpej * and the mode set to 0666 modified by the caller's umask. 764d21620b2Sthorpej */ 765d21620b2Sthorpej boolean_t 766d21620b2Sthorpej _prop_object_externalize_write_file(const char *fname, const char *xml, 767d21620b2Sthorpej size_t len) 768d21620b2Sthorpej { 769d21620b2Sthorpej char tname[PATH_MAX]; 770d21620b2Sthorpej int fd; 771d21620b2Sthorpej int save_errno; 772d21620b2Sthorpej 773d21620b2Sthorpej if (len > SSIZE_MAX) { 774d21620b2Sthorpej errno = EFBIG; 775d21620b2Sthorpej return (FALSE); 776d21620b2Sthorpej } 777d21620b2Sthorpej 778d21620b2Sthorpej /* 779d21620b2Sthorpej * Get the directory name where the file is to be written 780d21620b2Sthorpej * and create the temporary file. 781d21620b2Sthorpej * 782d21620b2Sthorpej * We don't use mkstemp() because mkstemp() always creates the 783d21620b2Sthorpej * file with mode 0600. We do, however, use mktemp() safely. 784d21620b2Sthorpej */ 785d21620b2Sthorpej again: 786d21620b2Sthorpej _prop_object_externalize_file_dirname(fname, tname); 787d21620b2Sthorpej if (strlcat(tname, "/.plistXXXXXX", sizeof(tname)) >= sizeof(tname)) { 788d21620b2Sthorpej errno = ENAMETOOLONG; 789d21620b2Sthorpej return (FALSE); 790d21620b2Sthorpej } 791d21620b2Sthorpej if (mktemp(tname) == NULL) 792d21620b2Sthorpej return (FALSE); 793d21620b2Sthorpej if ((fd = open(tname, O_CREAT|O_RDWR|O_EXCL, 0666)) == -1) { 794d21620b2Sthorpej if (errno == EEXIST) 795d21620b2Sthorpej goto again; 796d21620b2Sthorpej return (FALSE); 797d21620b2Sthorpej } 798d21620b2Sthorpej 799d21620b2Sthorpej if (write(fd, xml, len) != (ssize_t)len) 800d21620b2Sthorpej goto bad; 801d21620b2Sthorpej 802d21620b2Sthorpej if (fsync(fd) == -1) 803d21620b2Sthorpej goto bad; 804d21620b2Sthorpej 805d21620b2Sthorpej (void) close(fd); 806d21620b2Sthorpej fd = -1; 807d21620b2Sthorpej 808d21620b2Sthorpej if (rename(tname, fname) == -1) 809d21620b2Sthorpej goto bad; 810d21620b2Sthorpej 811d21620b2Sthorpej return (TRUE); 812d21620b2Sthorpej 813d21620b2Sthorpej bad: 814d21620b2Sthorpej save_errno = errno; 815d21620b2Sthorpej if (fd != -1) 816d21620b2Sthorpej (void) close(fd); 817d21620b2Sthorpej (void) unlink(tname); 818d21620b2Sthorpej errno = save_errno; 819d21620b2Sthorpej return (FALSE); 820d21620b2Sthorpej } 821d21620b2Sthorpej 822d21620b2Sthorpej /* 823d21620b2Sthorpej * _prop_object_internalize_map_file -- 824d21620b2Sthorpej * Map a file for the purpose of internalizing it. 825d21620b2Sthorpej */ 826d21620b2Sthorpej struct _prop_object_internalize_mapped_file * 827d21620b2Sthorpej _prop_object_internalize_map_file(const char *fname) 828d21620b2Sthorpej { 829d21620b2Sthorpej struct stat sb; 830d21620b2Sthorpej struct _prop_object_internalize_mapped_file *mf; 831d21620b2Sthorpej size_t pgsize = sysconf(_SC_PAGESIZE); 832d21620b2Sthorpej size_t pgmask = pgsize - 1; 833d21620b2Sthorpej boolean_t need_guard = FALSE; 834d21620b2Sthorpej int fd; 835d21620b2Sthorpej 836d21620b2Sthorpej mf = _PROP_MALLOC(sizeof(*mf), M_TEMP); 837d21620b2Sthorpej if (mf == NULL) 838d21620b2Sthorpej return (NULL); 839d21620b2Sthorpej 840d21620b2Sthorpej fd = open(fname, O_RDONLY, 0400); 841d21620b2Sthorpej if (fd == -1) { 842d21620b2Sthorpej _PROP_FREE(mf, M_TEMP); 843d21620b2Sthorpej return (NULL); 844d21620b2Sthorpej } 845d21620b2Sthorpej 846d21620b2Sthorpej if (fstat(fd, &sb) == -1) { 847d21620b2Sthorpej (void) close(fd); 848d21620b2Sthorpej _PROP_FREE(mf, M_TEMP); 849d21620b2Sthorpej return (NULL); 850d21620b2Sthorpej } 851d21620b2Sthorpej mf->poimf_mapsize = ((size_t)sb.st_size + pgmask) & ~pgmask; 852d21620b2Sthorpej if (mf->poimf_mapsize < sb.st_size) { 853d21620b2Sthorpej (void) close(fd); 854d21620b2Sthorpej _PROP_FREE(mf, M_TEMP); 855d21620b2Sthorpej return (NULL); 856d21620b2Sthorpej } 857d21620b2Sthorpej 858d21620b2Sthorpej /* 859d21620b2Sthorpej * If the file length is an integral number of pages, then we 860d21620b2Sthorpej * need to map a guard page at the end in order to provide the 861d21620b2Sthorpej * necessary NUL-termination of the buffer. 862d21620b2Sthorpej */ 863d21620b2Sthorpej if ((sb.st_size & pgmask) == 0) 864d21620b2Sthorpej need_guard = TRUE; 865d21620b2Sthorpej 866d21620b2Sthorpej mf->poimf_xml = mmap(NULL, need_guard ? mf->poimf_mapsize + pgsize 867d21620b2Sthorpej : mf->poimf_mapsize, 868d21620b2Sthorpej PROT_READ, MAP_FILE|MAP_SHARED, fd, (off_t)0); 869d21620b2Sthorpej (void) close(fd); 870d21620b2Sthorpej if (mf->poimf_xml == MAP_FAILED) { 871d21620b2Sthorpej _PROP_FREE(mf, M_TEMP); 872d21620b2Sthorpej return (NULL); 873d21620b2Sthorpej } 874d21620b2Sthorpej (void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_SEQUENTIAL); 875d21620b2Sthorpej 876d21620b2Sthorpej if (need_guard) { 877d21620b2Sthorpej if (mmap(mf->poimf_xml + mf->poimf_mapsize, 878d21620b2Sthorpej pgsize, PROT_READ, 879d21620b2Sthorpej MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1, 880d21620b2Sthorpej (off_t)0) == MAP_FAILED) { 881d21620b2Sthorpej (void) munmap(mf->poimf_xml, mf->poimf_mapsize); 882d21620b2Sthorpej _PROP_FREE(mf, M_TEMP); 883d21620b2Sthorpej return (NULL); 884d21620b2Sthorpej } 885d21620b2Sthorpej mf->poimf_mapsize += pgsize; 886d21620b2Sthorpej } 887d21620b2Sthorpej 888d21620b2Sthorpej return (mf); 889d21620b2Sthorpej } 890d21620b2Sthorpej 891d21620b2Sthorpej /* 892d21620b2Sthorpej * _prop_object_internalize_unmap_file -- 893d21620b2Sthorpej * Unmap a file previously mapped for internalizing. 894d21620b2Sthorpej */ 895d21620b2Sthorpej void 896d21620b2Sthorpej _prop_object_internalize_unmap_file( 897d21620b2Sthorpej struct _prop_object_internalize_mapped_file *mf) 898d21620b2Sthorpej { 899d21620b2Sthorpej 900d21620b2Sthorpej (void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_DONTNEED); 901d21620b2Sthorpej (void) munmap(mf->poimf_xml, mf->poimf_mapsize); 902d21620b2Sthorpej _PROP_FREE(mf, M_TEMP); 903d21620b2Sthorpej } 904d21620b2Sthorpej #endif /* !_KERNEL && !_STANDALONE */ 905d21620b2Sthorpej 906774eb1a3Sthorpej /* 907774eb1a3Sthorpej * Retain / release serialization -- 908774eb1a3Sthorpej * 909774eb1a3Sthorpej * Eventually we would like to use atomic operations. But until we have 910774eb1a3Sthorpej * an MI API for them that is common to userland and the kernel, we will 911774eb1a3Sthorpej * use a lock instead. 912774eb1a3Sthorpej * 913774eb1a3Sthorpej * We use a single global mutex for all serialization. In the kernel, because 914774eb1a3Sthorpej * we are still under a biglock, this will basically never contend (properties 915774eb1a3Sthorpej * cannot be manipulated at interrupt level). In userland, this will cost 916774eb1a3Sthorpej * nothing for single-threaded programs. For multi-threaded programs, there 917774eb1a3Sthorpej * could be contention, but it probably won't cost that much unless the program 918774eb1a3Sthorpej * makes heavy use of property lists. 919774eb1a3Sthorpej */ 920eb2acb85Sthorpej _PROP_MUTEX_DECL_STATIC(_prop_refcnt_mutex) 921eff71884Sthorpej #define _PROP_REFCNT_LOCK() _PROP_MUTEX_LOCK(_prop_refcnt_mutex) 922eff71884Sthorpej #define _PROP_REFCNT_UNLOCK() _PROP_MUTEX_UNLOCK(_prop_refcnt_mutex) 923774eb1a3Sthorpej 924774eb1a3Sthorpej /* 925774eb1a3Sthorpej * prop_object_retain -- 926774eb1a3Sthorpej * Increment the reference count on an object. 927774eb1a3Sthorpej */ 928774eb1a3Sthorpej void 929774eb1a3Sthorpej prop_object_retain(prop_object_t obj) 930774eb1a3Sthorpej { 931774eb1a3Sthorpej struct _prop_object *po = obj; 932774eb1a3Sthorpej uint32_t ocnt; 933774eb1a3Sthorpej 934774eb1a3Sthorpej _PROP_REFCNT_LOCK(); 935774eb1a3Sthorpej ocnt = po->po_refcnt++; 936774eb1a3Sthorpej _PROP_REFCNT_UNLOCK(); 937774eb1a3Sthorpej 938774eb1a3Sthorpej _PROP_ASSERT(ocnt != 0xffffffffU); 939774eb1a3Sthorpej } 940774eb1a3Sthorpej 941774eb1a3Sthorpej /* 942774eb1a3Sthorpej * prop_object_release -- 943774eb1a3Sthorpej * Decrement the reference count on an object. 944774eb1a3Sthorpej * 945774eb1a3Sthorpej * Free the object if we are releasing the final 946774eb1a3Sthorpej * reference. 947774eb1a3Sthorpej */ 948774eb1a3Sthorpej void 949774eb1a3Sthorpej prop_object_release(prop_object_t obj) 950774eb1a3Sthorpej { 951774eb1a3Sthorpej struct _prop_object *po = obj; 952774eb1a3Sthorpej uint32_t ocnt; 953774eb1a3Sthorpej 954774eb1a3Sthorpej _PROP_REFCNT_LOCK(); 955774eb1a3Sthorpej ocnt = po->po_refcnt--; 956774eb1a3Sthorpej _PROP_REFCNT_UNLOCK(); 957774eb1a3Sthorpej 958774eb1a3Sthorpej _PROP_ASSERT(ocnt != 0); 959774eb1a3Sthorpej if (ocnt == 1) 9603e69f1b2Sthorpej (*po->po_type->pot_free)(po); 961774eb1a3Sthorpej } 962774eb1a3Sthorpej 963774eb1a3Sthorpej /* 964774eb1a3Sthorpej * prop_object_type -- 965774eb1a3Sthorpej * Return the type of an object. 966774eb1a3Sthorpej */ 967774eb1a3Sthorpej prop_type_t 968774eb1a3Sthorpej prop_object_type(prop_object_t obj) 969774eb1a3Sthorpej { 970774eb1a3Sthorpej struct _prop_object *po = obj; 971774eb1a3Sthorpej 972d21620b2Sthorpej if (obj == NULL) 973d21620b2Sthorpej return (PROP_TYPE_UNKNOWN); 974d21620b2Sthorpej 9753e69f1b2Sthorpej return (po->po_type->pot_type); 9763e69f1b2Sthorpej } 9773e69f1b2Sthorpej 9783e69f1b2Sthorpej /* 9793e69f1b2Sthorpej * prop_object_equals -- 9803e69f1b2Sthorpej * Returns TRUE if thw two objects are equivalent. 9813e69f1b2Sthorpej */ 9823e69f1b2Sthorpej boolean_t 9833e69f1b2Sthorpej prop_object_equals(prop_object_t obj1, prop_object_t obj2) 9843e69f1b2Sthorpej { 9853e69f1b2Sthorpej struct _prop_object *po1 = obj1; 9863e69f1b2Sthorpej struct _prop_object *po2 = obj2; 9873e69f1b2Sthorpej 9883e69f1b2Sthorpej if (po1->po_type != po2->po_type) 9893e69f1b2Sthorpej return (FALSE); 9903e69f1b2Sthorpej 9913e69f1b2Sthorpej return ((*po1->po_type->pot_equals)(obj1, obj2)); 992774eb1a3Sthorpej } 993774eb1a3Sthorpej 994774eb1a3Sthorpej /* 995774eb1a3Sthorpej * prop_object_iterator_next -- 996774eb1a3Sthorpej * Return the next item during an iteration. 997774eb1a3Sthorpej */ 998774eb1a3Sthorpej prop_object_t 999774eb1a3Sthorpej prop_object_iterator_next(prop_object_iterator_t pi) 1000774eb1a3Sthorpej { 1001774eb1a3Sthorpej 1002774eb1a3Sthorpej return ((*pi->pi_next_object)(pi)); 1003774eb1a3Sthorpej } 1004774eb1a3Sthorpej 1005774eb1a3Sthorpej /* 1006774eb1a3Sthorpej * prop_object_iterator_reset -- 1007774eb1a3Sthorpej * Reset the iterator to the first object so as to restart 1008774eb1a3Sthorpej * iteration. 1009774eb1a3Sthorpej */ 1010774eb1a3Sthorpej void 1011774eb1a3Sthorpej prop_object_iterator_reset(prop_object_iterator_t pi) 1012774eb1a3Sthorpej { 1013774eb1a3Sthorpej 1014774eb1a3Sthorpej (*pi->pi_reset)(pi); 1015774eb1a3Sthorpej } 1016774eb1a3Sthorpej 1017774eb1a3Sthorpej /* 1018774eb1a3Sthorpej * prop_object_iterator_release -- 1019774eb1a3Sthorpej * Release the object iterator. 1020774eb1a3Sthorpej */ 1021774eb1a3Sthorpej void 1022774eb1a3Sthorpej prop_object_iterator_release(prop_object_iterator_t pi) 1023774eb1a3Sthorpej { 1024774eb1a3Sthorpej 1025774eb1a3Sthorpej prop_object_release(pi->pi_obj); 1026774eb1a3Sthorpej _PROP_FREE(pi, M_TEMP); 1027774eb1a3Sthorpej } 1028