1 /* $NetBSD: prop_object.c,v 1.7 2006/10/12 04:41:51 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <prop/prop_object.h> 40 #include "prop_object_impl.h" 41 42 #if !defined(_KERNEL) && !defined(_STANDALONE) 43 #include <sys/mman.h> 44 #include <sys/stat.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <limits.h> 48 #include <unistd.h> 49 #endif 50 51 #ifdef _STANDALONE 52 void * 53 _prop_standalone_calloc(size_t size) 54 { 55 void *rv; 56 57 rv = alloc(size); 58 if (rv != NULL) 59 memset(rv, 0, size); 60 61 return (rv); 62 } 63 64 void * 65 _prop_standalone_realloc(void *v, size_t size) 66 { 67 void *rv; 68 69 rv = alloc(size); 70 if (rv != NULL) { 71 memcpy(rv, v, size); /* XXX */ 72 dealloc(v, 0); /* XXX */ 73 } 74 75 return (rv); 76 } 77 #endif /* _STANDALONE */ 78 79 /* 80 * _prop_object_init -- 81 * Initialize an object. Called when sub-classes create 82 * an instance. 83 */ 84 void 85 _prop_object_init(struct _prop_object *po, const struct _prop_object_type *pot) 86 { 87 88 po->po_type = pot; 89 po->po_refcnt = 1; 90 } 91 92 /* 93 * _prop_object_fini -- 94 * Finalize an object. Called when sub-classes destroy 95 * an instance. 96 */ 97 /*ARGSUSED*/ 98 void 99 _prop_object_fini(struct _prop_object *po) 100 { 101 /* Nothing to do, currently. */ 102 (void) po; 103 } 104 105 /* 106 * _prop_object_externalize_start_tag -- 107 * Append an XML-style start tag to the externalize buffer. 108 */ 109 boolean_t 110 _prop_object_externalize_start_tag( 111 struct _prop_object_externalize_context *ctx, const char *tag) 112 { 113 unsigned int i; 114 115 for (i = 0; i < ctx->poec_depth; i++) { 116 if (_prop_object_externalize_append_char(ctx, '\t') == FALSE) 117 return (FALSE); 118 } 119 if (_prop_object_externalize_append_char(ctx, '<') == FALSE || 120 _prop_object_externalize_append_cstring(ctx, tag) == FALSE || 121 _prop_object_externalize_append_char(ctx, '>') == FALSE) 122 return (FALSE); 123 124 return (TRUE); 125 } 126 127 /* 128 * _prop_object_externalize_end_tag -- 129 * Append an XML-style end tag to the externalize buffer. 130 */ 131 boolean_t 132 _prop_object_externalize_end_tag( 133 struct _prop_object_externalize_context *ctx, const char *tag) 134 { 135 136 if (_prop_object_externalize_append_char(ctx, '<') == FALSE || 137 _prop_object_externalize_append_char(ctx, '/') == FALSE || 138 _prop_object_externalize_append_cstring(ctx, tag) == FALSE || 139 _prop_object_externalize_append_char(ctx, '>') == FALSE || 140 _prop_object_externalize_append_char(ctx, '\n') == FALSE) 141 return (FALSE); 142 143 return (TRUE); 144 } 145 146 /* 147 * _prop_object_externalize_empty_tag -- 148 * Append an XML-style empty tag to the externalize buffer. 149 */ 150 boolean_t 151 _prop_object_externalize_empty_tag( 152 struct _prop_object_externalize_context *ctx, const char *tag) 153 { 154 unsigned int i; 155 156 for (i = 0; i < ctx->poec_depth; i++) { 157 if (_prop_object_externalize_append_char(ctx, '\t') == FALSE) 158 return (FALSE); 159 } 160 161 if (_prop_object_externalize_append_char(ctx, '<') == FALSE || 162 _prop_object_externalize_append_cstring(ctx, tag) == FALSE || 163 _prop_object_externalize_append_char(ctx, '/') == FALSE || 164 _prop_object_externalize_append_char(ctx, '>') == FALSE || 165 _prop_object_externalize_append_char(ctx, '\n') == FALSE) 166 return (FALSE); 167 168 return (TRUE); 169 } 170 171 /* 172 * _prop_object_externalize_append_cstring -- 173 * Append a C string to the externalize buffer. 174 */ 175 boolean_t 176 _prop_object_externalize_append_cstring( 177 struct _prop_object_externalize_context *ctx, const char *cp) 178 { 179 180 while (*cp != '\0') { 181 if (_prop_object_externalize_append_char(ctx, 182 (unsigned char) *cp) == FALSE) 183 return (FALSE); 184 cp++; 185 } 186 187 return (TRUE); 188 } 189 190 /* 191 * _prop_object_externalize_append_encoded_cstring -- 192 * Append an encoded C string to the externalize buffer. 193 */ 194 boolean_t 195 _prop_object_externalize_append_encoded_cstring( 196 struct _prop_object_externalize_context *ctx, const char *cp) 197 { 198 199 while (*cp != '\0') { 200 switch (*cp) { 201 case '<': 202 if (_prop_object_externalize_append_cstring(ctx, 203 "<") == FALSE) 204 return (FALSE); 205 break; 206 case '>': 207 if (_prop_object_externalize_append_cstring(ctx, 208 ">") == FALSE) 209 return (FALSE); 210 break; 211 case '&': 212 if (_prop_object_externalize_append_cstring(ctx, 213 "&") == FALSE) 214 return (FALSE); 215 break; 216 default: 217 if (_prop_object_externalize_append_char(ctx, 218 (unsigned char) *cp) == FALSE) 219 return (FALSE); 220 break; 221 } 222 cp++; 223 } 224 225 return (TRUE); 226 } 227 228 #define BUF_EXPAND 256 229 230 /* 231 * _prop_object_externalize_append_char -- 232 * Append a single character to the externalize buffer. 233 */ 234 boolean_t 235 _prop_object_externalize_append_char( 236 struct _prop_object_externalize_context *ctx, unsigned char c) 237 { 238 239 _PROP_ASSERT(ctx->poec_capacity != 0); 240 _PROP_ASSERT(ctx->poec_buf != NULL); 241 _PROP_ASSERT(ctx->poec_len <= ctx->poec_capacity); 242 243 if (ctx->poec_len == ctx->poec_capacity) { 244 char *cp = _PROP_REALLOC(ctx->poec_buf, 245 ctx->poec_capacity + BUF_EXPAND, 246 M_TEMP); 247 if (cp == NULL) 248 return (FALSE); 249 ctx->poec_capacity = ctx->poec_capacity + BUF_EXPAND; 250 ctx->poec_buf = cp; 251 } 252 253 ctx->poec_buf[ctx->poec_len++] = c; 254 255 return (TRUE); 256 } 257 258 /* 259 * _prop_object_externalize_header -- 260 * Append the standard XML header to the externalize buffer. 261 */ 262 boolean_t 263 _prop_object_externalize_header(struct _prop_object_externalize_context *ctx) 264 { 265 static const char _plist_xml_header[] = 266 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 267 "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"; 268 269 if (_prop_object_externalize_append_cstring(ctx, 270 _plist_xml_header) == FALSE || 271 _prop_object_externalize_start_tag(ctx, 272 "plist version=\"1.0\"") == FALSE || 273 _prop_object_externalize_append_char(ctx, '\n') == FALSE) 274 return (FALSE); 275 276 return (TRUE); 277 } 278 279 /* 280 * _prop_object_externalize_footer -- 281 * Append the standard XML footer to the externalize buffer. This 282 * also NUL-terminates the buffer. 283 */ 284 boolean_t 285 _prop_object_externalize_footer(struct _prop_object_externalize_context *ctx) 286 { 287 288 if (_prop_object_externalize_end_tag(ctx, "plist") == FALSE || 289 _prop_object_externalize_append_char(ctx, '\0') == FALSE) 290 return (FALSE); 291 292 return (TRUE); 293 } 294 295 /* 296 * _prop_object_externalize_context_alloc -- 297 * Allocate an externalize context. 298 */ 299 struct _prop_object_externalize_context * 300 _prop_object_externalize_context_alloc(void) 301 { 302 struct _prop_object_externalize_context *ctx; 303 304 ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP); 305 if (ctx != NULL) { 306 ctx->poec_buf = _PROP_MALLOC(BUF_EXPAND, M_TEMP); 307 if (ctx->poec_buf == NULL) { 308 _PROP_FREE(ctx, M_TEMP); 309 return (NULL); 310 } 311 ctx->poec_len = 0; 312 ctx->poec_capacity = BUF_EXPAND; 313 ctx->poec_depth = 0; 314 } 315 return (ctx); 316 } 317 318 /* 319 * _prop_object_externalize_context_free -- 320 * Free an externalize context. 321 */ 322 void 323 _prop_object_externalize_context_free( 324 struct _prop_object_externalize_context *ctx) 325 { 326 327 /* Buffer is always freed by the caller. */ 328 _PROP_FREE(ctx, M_TEMP); 329 } 330 331 /* 332 * _prop_object_internalize_skip_comment -- 333 * Skip the body and end tag of a comment. 334 */ 335 static boolean_t 336 _prop_object_internalize_skip_comment( 337 struct _prop_object_internalize_context *ctx) 338 { 339 const char *cp = ctx->poic_cp; 340 341 while (!_PROP_EOF(*cp)) { 342 if (cp[0] == '-' && 343 cp[1] == '-' && 344 cp[2] == '>') { 345 ctx->poic_cp = cp + 3; 346 return (TRUE); 347 } 348 cp++; 349 } 350 351 return (FALSE); /* ran out of buffer */ 352 } 353 354 /* 355 * _prop_object_internalize_find_tag -- 356 * Find the next tag in an XML stream. Optionally compare the found 357 * tag to an expected tag name. State of the context is undefined 358 * if this routine returns FALSE. Upon success, the context points 359 * to the first octet after the tag. 360 */ 361 boolean_t 362 _prop_object_internalize_find_tag(struct _prop_object_internalize_context *ctx, 363 const char *tag, _prop_tag_type_t type) 364 { 365 const char *cp; 366 size_t taglen; 367 368 if (tag != NULL) 369 taglen = strlen(tag); 370 else 371 taglen = 0; 372 373 start_over: 374 cp = ctx->poic_cp; 375 376 /* 377 * Find the start of the tag. 378 */ 379 while (_PROP_ISSPACE(*cp)) 380 cp++; 381 if (_PROP_EOF(*cp)) 382 return (FALSE); 383 384 if (*cp != '<') 385 return (FALSE); 386 387 ctx->poic_tag_start = cp++; 388 if (_PROP_EOF(*cp)) 389 return (FALSE); 390 391 if (*cp == '!') { 392 if (cp[1] != '-' || cp[2] != '-') 393 return (FALSE); 394 /* 395 * Comment block -- only allowed if we are allowed to 396 * return a start tag. 397 */ 398 if (type == _PROP_TAG_TYPE_END) 399 return (FALSE); 400 ctx->poic_cp = cp + 3; 401 if (_prop_object_internalize_skip_comment(ctx) == FALSE) 402 return (FALSE); 403 goto start_over; 404 } 405 406 if (*cp == '/') { 407 if (type != _PROP_TAG_TYPE_END && 408 type != _PROP_TAG_TYPE_EITHER) 409 return (FALSE); 410 cp++; 411 if (_PROP_EOF(*cp)) 412 return (FALSE); 413 ctx->poic_tag_type = _PROP_TAG_TYPE_END; 414 } else { 415 if (type != _PROP_TAG_TYPE_START && 416 type != _PROP_TAG_TYPE_EITHER) 417 return (FALSE); 418 ctx->poic_tag_type = _PROP_TAG_TYPE_START; 419 } 420 421 ctx->poic_tagname = cp; 422 423 while (!_PROP_ISSPACE(*cp) && *cp != '/' && *cp != '>') 424 cp++; 425 if (_PROP_EOF(*cp)) 426 return (FALSE); 427 428 ctx->poic_tagname_len = cp - ctx->poic_tagname; 429 430 /* Make sure this is the tag we're looking for. */ 431 if (tag != NULL && 432 (taglen != ctx->poic_tagname_len || 433 memcmp(tag, ctx->poic_tagname, taglen) != 0)) 434 return (FALSE); 435 436 /* Check for empty tag. */ 437 if (*cp == '/') { 438 if (ctx->poic_tag_type != _PROP_TAG_TYPE_START) 439 return(FALSE); /* only valid on start tags */ 440 ctx->poic_is_empty_element = TRUE; 441 cp++; 442 if (_PROP_EOF(*cp) || *cp != '>') 443 return (FALSE); 444 } else 445 ctx->poic_is_empty_element = FALSE; 446 447 /* Easy case of no arguments. */ 448 if (*cp == '>') { 449 ctx->poic_tagattr = NULL; 450 ctx->poic_tagattr_len = 0; 451 ctx->poic_tagattrval = NULL; 452 ctx->poic_tagattrval_len = 0; 453 ctx->poic_cp = cp + 1; 454 return (TRUE); 455 } 456 457 _PROP_ASSERT(!_PROP_EOF(*cp)); 458 cp++; 459 if (_PROP_EOF(*cp)) 460 return (FALSE); 461 462 while (_PROP_ISSPACE(*cp)) 463 cp++; 464 if (_PROP_EOF(*cp)) 465 return (FALSE); 466 467 ctx->poic_tagattr = cp; 468 469 while (!_PROP_ISSPACE(*cp) && *cp != '=') 470 cp++; 471 if (_PROP_EOF(*cp)) 472 return (FALSE); 473 474 ctx->poic_tagattr_len = cp - ctx->poic_tagattr; 475 476 cp++; 477 if (*cp != '\"') 478 return (FALSE); 479 cp++; 480 if (_PROP_EOF(*cp)) 481 return (FALSE); 482 483 ctx->poic_tagattrval = cp; 484 while (*cp != '\"') 485 cp++; 486 if (_PROP_EOF(*cp)) 487 return (FALSE); 488 ctx->poic_tagattrval_len = cp - ctx->poic_tagattrval; 489 490 cp++; 491 if (*cp != '>') 492 return (FALSE); 493 494 ctx->poic_cp = cp + 1; 495 return (TRUE); 496 } 497 498 /* 499 * _prop_object_internalize_decode_string -- 500 * Decode an encoded string. 501 */ 502 boolean_t 503 _prop_object_internalize_decode_string( 504 struct _prop_object_internalize_context *ctx, 505 char *target, size_t targsize, size_t *sizep, 506 const char **cpp) 507 { 508 const char *src; 509 size_t tarindex; 510 char c; 511 512 tarindex = 0; 513 src = ctx->poic_cp; 514 515 for (;;) { 516 if (_PROP_EOF(*src)) 517 return (FALSE); 518 if (*src == '<') { 519 break; 520 } 521 522 if ((c = *src) == '&') { 523 if (src[1] == 'a' && 524 src[2] == 'm' && 525 src[3] == 'p' && 526 src[4] == ';') { 527 c = '&'; 528 src += 5; 529 } else if (src[1] == 'l' && 530 src[2] == 't' && 531 src[3] == ';') { 532 c = '<'; 533 src += 4; 534 } else if (src[1] == 'g' && 535 src[2] == 't' && 536 src[3] == ';') { 537 c = '>'; 538 src += 4; 539 } else if (src[1] == 'a' && 540 src[2] == 'p' && 541 src[3] == 'o' && 542 src[4] == 's' && 543 src[5] == ';') { 544 c = '\''; 545 src += 6; 546 } else if (src[1] == 'q' && 547 src[2] == 'u' && 548 src[3] == 'o' && 549 src[4] == 't' && 550 src[5] == ';') { 551 c = '\"'; 552 src += 6; 553 } else 554 return (FALSE); 555 } else 556 src++; 557 if (target) { 558 if (tarindex >= targsize) 559 return (FALSE); 560 target[tarindex] = c; 561 } 562 tarindex++; 563 } 564 565 _PROP_ASSERT(*src == '<'); 566 if (sizep != NULL) 567 *sizep = tarindex; 568 if (cpp != NULL) 569 *cpp = src; 570 571 return (TRUE); 572 } 573 574 /* 575 * _prop_object_internalize_match -- 576 * Returns true if the two character streams match. 577 */ 578 boolean_t 579 _prop_object_internalize_match(const char *str1, size_t len1, 580 const char *str2, size_t len2) 581 { 582 583 return (len1 == len2 && memcmp(str1, str2, len1) == 0); 584 } 585 586 #define INTERNALIZER(t, f) \ 587 { t, sizeof(t) - 1, f } 588 589 static const struct _prop_object_internalizer { 590 const char *poi_tag; 591 size_t poi_taglen; 592 prop_object_t (*poi_intern)( 593 struct _prop_object_internalize_context *); 594 } _prop_object_internalizer_table[] = { 595 INTERNALIZER("array", _prop_array_internalize), 596 597 INTERNALIZER("true", _prop_bool_internalize), 598 INTERNALIZER("false", _prop_bool_internalize), 599 600 INTERNALIZER("data", _prop_data_internalize), 601 602 INTERNALIZER("dict", _prop_dictionary_internalize), 603 604 INTERNALIZER("integer", _prop_number_internalize), 605 606 INTERNALIZER("string", _prop_string_internalize), 607 608 { 0, 0, NULL } 609 }; 610 611 #undef INTERNALIZER 612 613 /* 614 * _prop_object_internalize_by_tag -- 615 * Determine the object type from the tag in the context and 616 * internalize it. 617 */ 618 prop_object_t 619 _prop_object_internalize_by_tag(struct _prop_object_internalize_context *ctx) 620 { 621 const struct _prop_object_internalizer *poi; 622 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 return ((*poi->poi_intern)(ctx)); 630 } 631 632 return (NULL); 633 } 634 635 /* 636 * _prop_object_internalize_context_alloc -- 637 * Allocate an internalize context. 638 */ 639 struct _prop_object_internalize_context * 640 _prop_object_internalize_context_alloc(const char *xml) 641 { 642 struct _prop_object_internalize_context *ctx; 643 644 ctx = _PROP_MALLOC(sizeof(struct _prop_object_internalize_context), 645 M_TEMP); 646 if (ctx == NULL) 647 return (NULL); 648 649 ctx->poic_xml = ctx->poic_cp = xml; 650 651 /* 652 * Skip any whitespace and XML preamble stuff that we don't 653 * know about / care about. 654 */ 655 for (;;) { 656 while (_PROP_ISSPACE(*xml)) 657 xml++; 658 if (_PROP_EOF(*xml) || *xml != '<') 659 goto bad; 660 661 #define MATCH(str) (memcmp(&xml[1], str, sizeof(str) - 1) == 0) 662 663 /* 664 * Skip over the XML preamble that Apple XML property 665 * lists usually include at the top of the file. 666 */ 667 if (MATCH("?xml ") || 668 MATCH("!DOCTYPE plist")) { 669 while (*xml != '>' && !_PROP_EOF(*xml)) 670 xml++; 671 if (_PROP_EOF(*xml)) 672 goto bad; 673 xml++; /* advance past the '>' */ 674 continue; 675 } 676 677 if (MATCH("<!--")) { 678 ctx->poic_cp = xml + 4; 679 if (_prop_object_internalize_skip_comment(ctx) == FALSE) 680 goto bad; 681 xml = ctx->poic_cp; 682 continue; 683 } 684 685 #undef MATCH 686 687 /* 688 * We don't think we should skip it, so let's hope we can 689 * parse it. 690 */ 691 break; 692 } 693 694 ctx->poic_cp = xml; 695 return (ctx); 696 bad: 697 _PROP_FREE(ctx, M_TEMP); 698 return (NULL); 699 } 700 701 /* 702 * _prop_object_internalize_context_free -- 703 * Free an internalize context. 704 */ 705 void 706 _prop_object_internalize_context_free( 707 struct _prop_object_internalize_context *ctx) 708 { 709 710 _PROP_FREE(ctx, M_TEMP); 711 } 712 713 #if !defined(_KERNEL) && !defined(_STANDALONE) 714 /* 715 * _prop_object_externalize_file_dirname -- 716 * dirname(3), basically. We have to roll our own because the 717 * system dirname(3) isn't reentrant. 718 */ 719 static void 720 _prop_object_externalize_file_dirname(const char *path, char *result) 721 { 722 const char *lastp; 723 size_t len; 724 725 /* 726 * If `path' is a NULL pointer or points to an empty string, 727 * return ".". 728 */ 729 if (path == NULL || *path == '\0') 730 goto singledot; 731 732 /* String trailing slashes, if any. */ 733 lastp = path + strlen(path) - 1; 734 while (lastp != path && *lastp == '/') 735 lastp--; 736 737 /* Terminate path at the last occurrence of '/'. */ 738 do { 739 if (*lastp == '/') { 740 /* Strip trailing slashes, if any. */ 741 while (lastp != path && *lastp == '/') 742 lastp--; 743 744 /* ...and copy the result into the result buffer. */ 745 len = (lastp - path) + 1 /* last char */; 746 if (len > (PATH_MAX - 1)) 747 len = PATH_MAX - 1; 748 749 memcpy(result, path, len); 750 result[len] = '\0'; 751 return; 752 } 753 } while (--lastp >= path); 754 755 /* No /'s found, return ".". */ 756 singledot: 757 strcpy(result, "."); 758 } 759 760 /* 761 * _prop_object_externalize_write_file -- 762 * Write an externalized dictionary to the specified file. 763 * The file is written atomically from the caller's perspective, 764 * and the mode set to 0666 modified by the caller's umask. 765 */ 766 boolean_t 767 _prop_object_externalize_write_file(const char *fname, const char *xml, 768 size_t len) 769 { 770 char tname[PATH_MAX]; 771 int fd; 772 int save_errno; 773 774 if (len > SSIZE_MAX) { 775 errno = EFBIG; 776 return (FALSE); 777 } 778 779 /* 780 * Get the directory name where the file is to be written 781 * and create the temporary file. 782 * 783 * We don't use mkstemp() because mkstemp() always creates the 784 * file with mode 0600. We do, however, use mktemp() safely. 785 */ 786 again: 787 _prop_object_externalize_file_dirname(fname, tname); 788 if (strlcat(tname, "/.plistXXXXXX", sizeof(tname)) >= sizeof(tname)) { 789 errno = ENAMETOOLONG; 790 return (FALSE); 791 } 792 if (mktemp(tname) == NULL) 793 return (FALSE); 794 if ((fd = open(tname, O_CREAT|O_RDWR|O_EXCL, 0666)) == -1) { 795 if (errno == EEXIST) 796 goto again; 797 return (FALSE); 798 } 799 800 if (write(fd, xml, len) != (ssize_t)len) 801 goto bad; 802 803 if (fsync(fd) == -1) 804 goto bad; 805 806 (void) close(fd); 807 fd = -1; 808 809 if (rename(tname, fname) == -1) 810 goto bad; 811 812 return (TRUE); 813 814 bad: 815 save_errno = errno; 816 if (fd != -1) 817 (void) close(fd); 818 (void) unlink(tname); 819 errno = save_errno; 820 return (FALSE); 821 } 822 823 /* 824 * _prop_object_internalize_map_file -- 825 * Map a file for the purpose of internalizing it. 826 */ 827 struct _prop_object_internalize_mapped_file * 828 _prop_object_internalize_map_file(const char *fname) 829 { 830 struct stat sb; 831 struct _prop_object_internalize_mapped_file *mf; 832 size_t pgsize = sysconf(_SC_PAGESIZE); 833 size_t pgmask = pgsize - 1; 834 boolean_t need_guard = FALSE; 835 int fd; 836 837 mf = _PROP_MALLOC(sizeof(*mf), M_TEMP); 838 if (mf == NULL) 839 return (NULL); 840 841 fd = open(fname, O_RDONLY, 0400); 842 if (fd == -1) { 843 _PROP_FREE(mf, M_TEMP); 844 return (NULL); 845 } 846 847 if (fstat(fd, &sb) == -1) { 848 (void) close(fd); 849 _PROP_FREE(mf, M_TEMP); 850 return (NULL); 851 } 852 mf->poimf_mapsize = ((size_t)sb.st_size + pgmask) & ~pgmask; 853 if (mf->poimf_mapsize < sb.st_size) { 854 (void) close(fd); 855 _PROP_FREE(mf, M_TEMP); 856 return (NULL); 857 } 858 859 /* 860 * If the file length is an integral number of pages, then we 861 * need to map a guard page at the end in order to provide the 862 * necessary NUL-termination of the buffer. 863 */ 864 if ((sb.st_size & pgmask) == 0) 865 need_guard = TRUE; 866 867 mf->poimf_xml = mmap(NULL, need_guard ? mf->poimf_mapsize + pgsize 868 : mf->poimf_mapsize, 869 PROT_READ, MAP_FILE|MAP_SHARED, fd, (off_t)0); 870 (void) close(fd); 871 if (mf->poimf_xml == MAP_FAILED) { 872 _PROP_FREE(mf, M_TEMP); 873 return (NULL); 874 } 875 (void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_SEQUENTIAL); 876 877 if (need_guard) { 878 if (mmap(mf->poimf_xml + mf->poimf_mapsize, 879 pgsize, PROT_READ, 880 MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1, 881 (off_t)0) == MAP_FAILED) { 882 (void) munmap(mf->poimf_xml, mf->poimf_mapsize); 883 _PROP_FREE(mf, M_TEMP); 884 return (NULL); 885 } 886 mf->poimf_mapsize += pgsize; 887 } 888 889 return (mf); 890 } 891 892 /* 893 * _prop_object_internalize_unmap_file -- 894 * Unmap a file previously mapped for internalizing. 895 */ 896 void 897 _prop_object_internalize_unmap_file( 898 struct _prop_object_internalize_mapped_file *mf) 899 { 900 901 (void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_DONTNEED); 902 (void) munmap(mf->poimf_xml, mf->poimf_mapsize); 903 _PROP_FREE(mf, M_TEMP); 904 } 905 #endif /* !_KERNEL && !_STANDALONE */ 906 907 /* 908 * Retain / release serialization -- 909 * 910 * Eventually we would like to use atomic operations. But until we have 911 * an MI API for them that is common to userland and the kernel, we will 912 * use a lock instead. 913 * 914 * We use a single global mutex for all serialization. In the kernel, because 915 * we are still under a biglock, this will basically never contend (properties 916 * cannot be manipulated at interrupt level). In userland, this will cost 917 * nothing for single-threaded programs. For multi-threaded programs, there 918 * could be contention, but it probably won't cost that much unless the program 919 * makes heavy use of property lists. 920 */ 921 _PROP_MUTEX_DECL_STATIC(_prop_refcnt_mutex) 922 #define _PROP_REFCNT_LOCK() _PROP_MUTEX_LOCK(_prop_refcnt_mutex) 923 #define _PROP_REFCNT_UNLOCK() _PROP_MUTEX_UNLOCK(_prop_refcnt_mutex) 924 925 /* 926 * prop_object_retain -- 927 * Increment the reference count on an object. 928 */ 929 void 930 prop_object_retain(prop_object_t obj) 931 { 932 struct _prop_object *po = obj; 933 uint32_t ocnt; 934 935 _PROP_REFCNT_LOCK(); 936 ocnt = po->po_refcnt++; 937 _PROP_REFCNT_UNLOCK(); 938 939 _PROP_ASSERT(ocnt != 0xffffffffU); 940 } 941 942 /* 943 * prop_object_release -- 944 * Decrement the reference count on an object. 945 * 946 * Free the object if we are releasing the final 947 * reference. 948 */ 949 void 950 prop_object_release(prop_object_t obj) 951 { 952 struct _prop_object *po = obj; 953 uint32_t ocnt; 954 955 _PROP_REFCNT_LOCK(); 956 ocnt = po->po_refcnt--; 957 _PROP_REFCNT_UNLOCK(); 958 959 _PROP_ASSERT(ocnt != 0); 960 if (ocnt == 1) 961 (*po->po_type->pot_free)(po); 962 } 963 964 /* 965 * prop_object_type -- 966 * Return the type of an object. 967 */ 968 prop_type_t 969 prop_object_type(prop_object_t obj) 970 { 971 struct _prop_object *po = obj; 972 973 if (obj == NULL) 974 return (PROP_TYPE_UNKNOWN); 975 976 return (po->po_type->pot_type); 977 } 978 979 /* 980 * prop_object_equals -- 981 * Returns TRUE if thw two objects are equivalent. 982 */ 983 boolean_t 984 prop_object_equals(prop_object_t obj1, prop_object_t obj2) 985 { 986 struct _prop_object *po1 = obj1; 987 struct _prop_object *po2 = obj2; 988 989 if (po1->po_type != po2->po_type) 990 return (FALSE); 991 992 return ((*po1->po_type->pot_equals)(obj1, obj2)); 993 } 994 995 /* 996 * prop_object_iterator_next -- 997 * Return the next item during an iteration. 998 */ 999 prop_object_t 1000 prop_object_iterator_next(prop_object_iterator_t pi) 1001 { 1002 1003 return ((*pi->pi_next_object)(pi)); 1004 } 1005 1006 /* 1007 * prop_object_iterator_reset -- 1008 * Reset the iterator to the first object so as to restart 1009 * iteration. 1010 */ 1011 void 1012 prop_object_iterator_reset(prop_object_iterator_t pi) 1013 { 1014 1015 (*pi->pi_reset)(pi); 1016 } 1017 1018 /* 1019 * prop_object_iterator_release -- 1020 * Release the object iterator. 1021 */ 1022 void 1023 prop_object_iterator_release(prop_object_iterator_t pi) 1024 { 1025 1026 prop_object_release(pi->pi_obj); 1027 _PROP_FREE(pi, M_TEMP); 1028 } 1029