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