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