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