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