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#endif 43#include <sys/atomic.h> 44 45#ifdef _STANDALONE 46void * 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 58void * 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 */ 78void 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*/ 92void 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 */ 102bool 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 */ 124bool 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 */ 143bool 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 */ 168bool 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 */ 187bool 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 */ 227bool 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 */ 255bool 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 */ 277bool 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 */ 292struct _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 */ 315void 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 */ 328static 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 */ 354bool 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 */ 495bool 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 */ 571bool 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 582static 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 */ 610prop_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 621match_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 654prop_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 */ 710struct _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 */ 776void 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 */ 790static 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 */ 837bool 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 */ 895struct _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 */ 964void 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 */ 979void 980prop_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 */ 998static void 999prop_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 */ 1054void 1055prop_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 */ 1106prop_type_t 1107prop_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 */ 1121bool 1122prop_object_equals(prop_object_t obj1, prop_object_t obj2) 1123{ 1124 return (prop_object_equals_with_error(obj1, obj2, NULL)); 1125} 1126 1127bool 1128prop_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 po1 = obj1; 1162 po2 = obj2; 1163 goto continue_subtree; 1164 } 1165 _PROP_ASSERT(ret == _PROP_OBJECT_EQUALS_RECURSE); 1166 1167 if (!_prop_stack_push(&stack, obj1, obj2, 1168 stored_pointer1, stored_pointer2)) { 1169 if (error_flag) 1170 *error_flag = true; 1171 goto finish; 1172 } 1173 obj1 = next_obj1; 1174 obj2 = next_obj2; 1175 goto start_subtree; 1176 1177finish: 1178 while (_prop_stack_pop(&stack, &obj1, &obj2, NULL, NULL)) { 1179 po1 = obj1; 1180 (*po1->po_type->pot_equals_finish)(obj1, obj2); 1181 } 1182 return (false); 1183} 1184 1185/* 1186 * prop_object_iterator_next -- 1187 * Return the next item during an iteration. 1188 */ 1189prop_object_t 1190prop_object_iterator_next(prop_object_iterator_t pi) 1191{ 1192 1193 return ((*pi->pi_next_object)(pi)); 1194} 1195 1196/* 1197 * prop_object_iterator_reset -- 1198 * Reset the iterator to the first object so as to restart 1199 * iteration. 1200 */ 1201void 1202prop_object_iterator_reset(prop_object_iterator_t pi) 1203{ 1204 1205 (*pi->pi_reset)(pi); 1206} 1207 1208/* 1209 * prop_object_iterator_release -- 1210 * Release the object iterator. 1211 */ 1212void 1213prop_object_iterator_release(prop_object_iterator_t pi) 1214{ 1215 1216 prop_object_release(pi->pi_obj); 1217 _PROP_FREE(pi, M_TEMP); 1218} 1219