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