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