1/* 2 * Copyright (c) 1999-2007 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25#include <device/device_types.h> 26#include <CoreFoundation/CoreFoundation.h> 27#include <IOKit/IOCFSerialize.h> 28#include <IOKit/IOCFUnserialize.h> 29 30#if IOKIT_SERVER_VERSION >= 20140421 31#include <System/libkern/OSSerializeBinary.h> 32#endif /* IOKIT_SERVER_VERSION >= 20140421 */ 33 34#include <assert.h> 35#include <syslog.h> 36 37typedef struct { 38 CFMutableDataRef data; 39 40 int idrefNumRefs; 41 42/* For each CFType, we track whether a given plist value hasRefs, 43 * and what the ids used for those refs are. 'hasRefs' is set to 44 * kCFBooleanFalse the first time we see a given value, so we know 45 * we saw it, and then kCFBooleanTrue if we see it again, so we know 46 * we need an id for it. On writing out the XML, we generate ids as 47 * we encounter the need. 48 */ 49 CFMutableDictionaryRef stringIDRefDictionary; 50 CFMutableDictionaryRef numberIDRefDictionary; 51 CFMutableDictionaryRef dataIDRefDictionary; 52 CFMutableDictionaryRef dictionaryIDRefDictionary; 53 CFMutableDictionaryRef arrayIDRefDictionary; 54 CFMutableDictionaryRef setIDRefDictionary; 55 56} IOCFSerializeState; 57 58 59static Boolean 60DoCFSerialize(CFTypeRef object, IOCFSerializeState * state); 61 62static CFDataRef 63IOCFSerializeBinary(CFTypeRef object, CFOptionFlags options); 64 65static Boolean 66addChar(char chr, IOCFSerializeState * state) 67{ 68 CFDataAppendBytes(state->data, (UInt8 *) &chr, 1); 69 70 return true; 71} 72 73static Boolean 74addString(const char * str, IOCFSerializeState * state) 75{ 76 CFIndex len = strlen(str); 77 CFDataAppendBytes(state->data, (UInt8 *) str, len); 78 79 return true; 80} 81 82static const char * 83getTagString(CFTypeRef object) 84{ 85 CFTypeID type; 86 87 assert(object); 88 89 type = CFGetTypeID(object); 90 91 if (type == CFStringGetTypeID()) return "string"; 92 if (type == CFNumberGetTypeID()) return "integer"; 93 if (type == CFDataGetTypeID()) return "data"; 94 if (type == CFDictionaryGetTypeID()) return "dict"; 95 if (type == CFArrayGetTypeID()) return "array"; 96 if (type == CFSetGetTypeID()) return "set"; 97 98 return "internal error"; 99} 100 101static CFMutableDictionaryRef 102idRefDictionaryForObject( 103 CFTypeRef object, 104 IOCFSerializeState * state) 105{ 106 CFMutableDictionaryRef result = NULL; 107 CFTypeID objectType = CFNullGetTypeID(); // do not release 108 109 objectType = CFGetTypeID(object); 110 111 /* Sorted by rough order of % occurence in big plists. 112 */ 113 if (objectType == CFDictionaryGetTypeID()) { 114 result = state->dictionaryIDRefDictionary; 115 } else if (objectType == CFStringGetTypeID()) { 116 result = state->stringIDRefDictionary; 117 } else if (objectType == CFArrayGetTypeID()) { 118 result = state->arrayIDRefDictionary; 119 } else if (objectType == CFNumberGetTypeID()) { 120 result = state->numberIDRefDictionary; 121 } else if (objectType == CFDataGetTypeID()) { 122 result = state->dataIDRefDictionary; 123 } else if (objectType == CFSetGetTypeID()) { 124 result = state->setIDRefDictionary; 125 } 126 127 return result; 128} 129 130void 131recordObjectInIDRefDictionary( 132 CFTypeRef object, 133 IOCFSerializeState * state) 134{ 135 CFTypeRef refEntry = NULL; // do not release 136 CFMutableDictionaryRef idRefDictionary = NULL; // do not release 137 138 if (!object || !state) { 139 goto finish; 140 } 141 142 idRefDictionary = idRefDictionaryForObject(object, state); 143 if (!idRefDictionary) { 144 goto finish; 145 } 146 147 refEntry = CFDictionaryGetValue(idRefDictionary, object); 148 149 /* If we have never seen this object value, then add an entry 150 * in the dictionary with value false, indicating we have seen 151 * it once. 152 * 153 * If we have seen this object value, then set its entry to true 154 * to indicate that we have now seen a second occurrence 155 * of the object value, which means we will generate an ID and IDREFs 156 * in the XML. 157 */ 158 if (!refEntry) { 159 /* Use AddValue here so as not to overwrite. 160 */ 161 CFDictionaryAddValue(idRefDictionary, object, kCFBooleanFalse); 162 } else { 163 CFDictionarySetValue(idRefDictionary, object, kCFBooleanTrue); 164 } 165 166finish: 167 return; 168} 169 170Boolean 171previouslySerialized( 172 CFTypeRef object, 173 IOCFSerializeState * state) 174{ 175 Boolean result = FALSE; 176 CFMutableDictionaryRef idRefDict = NULL; // do not release 177 CFTypeRef idRefEntry = NULL; // do not release 178 CFTypeID idRefType = CFNullGetTypeID(); // do not release 179 char temp[64]; 180 int idInt = -1; 181 182 if (!object || !state) { 183 goto finish; 184 } 185 186 /* If we don't get a lookup dict or an entry for the object, 187 * then no ID or IDREF will be involved, 188 * so treat is if never before serialized. 189 */ 190 idRefDict = idRefDictionaryForObject(object, state); 191 if (!idRefDict) { 192 goto finish; 193 } 194 195 idRefEntry = (CFTypeRef)CFDictionaryGetValue(idRefDict, object); 196 if (!idRefEntry) { 197 goto finish; 198 } 199 200 idRefType = CFGetTypeID(idRefEntry); 201 202 /* If the entry is of Boolean type, then an ID/IDREF may be involved, 203 * but we haven't created one yet, so not yet serialized. 204 */ 205 if (idRefType == CFBooleanGetTypeID()) { 206 goto finish; 207 } 208 209 /* At this point the entry should be a CFNumber. 210 */ 211 if (idRefType != CFNumberGetTypeID()) { 212 goto finish; 213 } 214 215 /* Finally, get the IDREF value out of the idRef entry and write the 216 * XML for it. Bail on extraction error. 217 */ 218 if (!CFNumberGetValue((CFNumberRef)idRefEntry, kCFNumberIntType, &idInt)) { 219 goto finish; 220 } 221 snprintf(temp, sizeof(temp), "<%s IDREF=\"%d\"/>", getTagString(object), idInt); 222 result = addString(temp, state); 223 224finish: 225 return result; 226} 227 228static Boolean 229addStartTag( 230 CFTypeRef object, 231 const char * additionalTags, 232 IOCFSerializeState * state) 233{ 234 CFMutableDictionaryRef idRefDict = NULL; // do not release 235 CFTypeRef idRefEntry = NULL; // do not release 236 CFNumberRef idRef = NULL; // must release 237 char temp[128]; 238 239 idRefDict = idRefDictionaryForObject(object, state); 240 if (idRefDict) { 241 idRefEntry = (CFTypeRef)CFDictionaryGetValue(idRefDict, object); 242 } 243 244 /* If the IDRef entry is 'true', then we know we have an object value 245 * with multiple references and need to emit an ID. So we create one 246 * by incrementing the state's counter, making a CFNumber, and *replacing* 247 * the boolean value in the IDRef dictionary with that number. 248 */ 249 if (idRefEntry == kCFBooleanTrue) { 250 int idInt = state->idrefNumRefs++; 251 252 idRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &idInt); 253 assert(idRef); 254 255 CFDictionarySetValue(idRefDict, object, idRef); 256 CFRelease(idRef); 257 258 if (additionalTags) { 259 snprintf(temp, sizeof(temp) * sizeof(char), 260 "<%s ID=\"%d\" %s>", getTagString(object), idInt, additionalTags); 261 } else { 262 snprintf(temp, sizeof(temp) * sizeof(char), 263 "<%s ID=\"%d\">", getTagString(object), idInt); 264 } 265 } else { 266 if (additionalTags) { 267 snprintf(temp, sizeof(temp) * sizeof(char), 268 "<%s %s>", getTagString(object), additionalTags); 269 } else { 270 snprintf(temp, sizeof(temp) * sizeof(char), 271 "<%s>", getTagString(object)); 272 } 273 } 274 275 return addString(temp, state); 276} 277 278 279static Boolean 280addEndTag(CFTypeRef object, 281 IOCFSerializeState * state) 282{ 283 char temp[128]; 284 285 snprintf(temp, sizeof(temp) * sizeof(char), "</%s>", getTagString(object)); 286 287 return addString(temp, state); 288} 289 290static Boolean 291DoCFSerializeNumber(CFNumberRef object, IOCFSerializeState * state) 292{ 293 char temp[32]; 294 long long value; 295 int size; 296 297 if (previouslySerialized(object, state)) return true; 298 299 if (!CFNumberGetValue(object, kCFNumberLongLongType, &value)) { 300 return(false); 301 } 302 303 switch(CFNumberGetType(object)) { 304 case kCFNumberSInt8Type: 305 case kCFNumberCharType: 306 size = 8; 307 break; 308 309 case kCFNumberSInt16Type: 310 case kCFNumberShortType: 311 size = 16; 312 break; 313 314 case kCFNumberSInt32Type: 315 case kCFNumberIntType: 316 size = 32; 317 break; 318 319 case kCFNumberLongType: 320#if __LP64__ 321 size = 64; 322#else 323 size = 32; 324#endif 325 break; 326 327 case kCFNumberSInt64Type: 328 case kCFNumberLongLongType: 329 default: 330 size = 64; 331 break; 332 } 333 334 snprintf(temp, sizeof(temp), "size=\"%d\"", size); 335 if (!addStartTag(object, temp, state)) return false; 336 337 if (size <= 32) 338 snprintf(temp, sizeof(temp), "0x%lx", (unsigned long int) value); 339 else 340 snprintf(temp, sizeof(temp), "0x%qx", value); 341 342 if (!addString(temp, state)) return false; 343 344 return addEndTag(object, state); 345} 346 347static Boolean 348DoCFSerializeBoolean(CFBooleanRef object, IOCFSerializeState * state) 349{ 350 return addString(CFBooleanGetValue(object) ? "<true/>" : "<false/>", state); 351} 352 353static Boolean 354DoCFSerializeString(CFStringRef object, IOCFSerializeState * state) 355{ 356 CFDataRef dataBuffer = 0; 357 const char *buffer = ""; 358 CFIndex length = 0; 359 char c; 360 int i; 361 Boolean succeeded = true; 362 363 if (previouslySerialized(object, state)) return true; 364 365 if (!addStartTag(object, 0, state)) return false; 366 367 dataBuffer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, object, kCFStringEncodingUTF8, '?'); 368 369 if (dataBuffer) { 370 length = CFDataGetLength(dataBuffer); 371 buffer = (char *) CFDataGetBytePtr(dataBuffer); 372 } 373 374 // this works because all bytes in a multi-byte utf-8 character have the high order bit set 375 for (i = 0; (i < length) && succeeded; i++) { 376 c = buffer[i]; 377 switch (c) { 378 case '<': 379 succeeded = addString("<", state); 380 break; 381 case '>': 382 succeeded = addString(">", state); 383 break; 384 case '&': 385 succeeded = addString("&", state); 386 break; 387 default: 388 succeeded = addChar(c, state); 389 break; 390 } 391 } 392 393 if (dataBuffer) CFRelease(dataBuffer); 394 395 if (succeeded) 396 return addEndTag(object, state); 397 else 398 return false; 399} 400 401static Boolean 402DoCFSerializeKey(CFStringRef object, IOCFSerializeState * state) 403{ 404 CFDataRef dataBuffer = 0; 405 const char *buffer = ""; 406 CFIndex length = 0; 407 char c; 408 int i; 409 Boolean succeeded = true; 410 411 if (!addString("<key>", state)) return false; 412 413 dataBuffer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, object, kCFStringEncodingUTF8, '?'); 414 415 if (dataBuffer) { 416 length = CFDataGetLength(dataBuffer); 417 buffer = (char *) CFDataGetBytePtr(dataBuffer); 418 } 419 420 // this works because all bytes in a multi-byte utf-8 character have the high order bit set 421 for (i = 0; (i < length) && succeeded; i++) { 422 c = buffer[i]; 423 switch (c) { 424 case '<': 425 succeeded = addString("<", state); 426 break; 427 case '>': 428 succeeded = addString(">", state); 429 break; 430 case '&': 431 succeeded = addString("&", state); 432 break; 433 default: 434 succeeded = addChar(c, state); 435 break; 436 } 437 } 438 439 if (dataBuffer) CFRelease(dataBuffer); 440 441 if (succeeded) 442 return addString("</key>", state); 443 else 444 return false; 445} 446 447//this was taken from CFPropertyList.c 448static const char __CFPLDataEncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 449 450static Boolean 451DoCFSerializeData(CFDataRef object, IOCFSerializeState * state) 452{ 453 CFIndex length; 454 const UInt8 * bytes; 455 CFIndex i; 456 const unsigned char *p; 457 unsigned char c = 0; 458 459 if (previouslySerialized(object, state)) return true; 460 461 length = CFDataGetLength(object); 462 bytes = CFDataGetBytePtr(object); 463 464 if (!addStartTag(object, 0, state)) return false; 465 466 for (i = 0, p = (unsigned char *)bytes; i < length; i++, p++) { 467 /* 3 bytes are encoded as 4 */ 468 switch (i % 3) { 469 case 0: 470 c = __CFPLDataEncodeTable [ ((p[0] >> 2) & 0x3f)]; 471 if (!addChar(c, state)) return false; 472 break; 473 case 1: 474 c = __CFPLDataEncodeTable [ ((((p[-1] << 8) | p[0]) >> 4) & 0x3f)]; 475 if (!addChar(c, state)) return false; 476 break; 477 case 2: 478 c = __CFPLDataEncodeTable [ ((((p[-1] << 8) | p[0]) >> 6) & 0x3f)]; 479 if (!addChar(c, state)) return false; 480 c = __CFPLDataEncodeTable [ (p[0] & 0x3f)]; 481 if (!addChar(c, state)) return false; 482 break; 483 } 484 } 485 switch (i % 3) { 486 case 0: 487 break; 488 case 1: 489 c = __CFPLDataEncodeTable [ ((p[-1] << 4) & 0x30)]; 490 if (!addChar(c, state)) return false; 491 if (!addChar('=', state)) return false; 492 if (!addChar('=', state)) return false; 493 break; 494 case 2: 495 c = __CFPLDataEncodeTable [ ((p[-1] << 2) & 0x3c)]; 496 if (!addChar(c, state)) return false; 497 if (!addChar('=', state)) return false; 498 break; 499 } 500 501 return addEndTag(object, state); 502} 503 504static Boolean 505DoCFSerializeArray(CFArrayRef object, IOCFSerializeState * state) 506{ 507 CFIndex count, i; 508 Boolean ok = true; 509 510 if (previouslySerialized(object, state)) return true; 511 512 if (!addStartTag(object, 0, state)) return false; 513 514 count = CFArrayGetCount(object); 515 if (count) { 516 for (i = 0; ok && (i < count); i++) { 517 ok = DoCFSerialize((CFTypeRef) CFArrayGetValueAtIndex(object, i), 518 state); 519 } 520 } 521 522 return ok && addEndTag(object, state); 523} 524 525static Boolean 526DoCFSerializeSet(CFSetRef object, IOCFSerializeState * state) 527{ 528 CFIndex count, i; 529 const void ** values; 530 Boolean ok = true; 531 532 if (previouslySerialized(object, state)) return true; 533 534 if (!addStartTag(object, 0, state)) return false; 535 536 count = CFSetGetCount(object); 537 538 if (count) { 539 values = (const void **) malloc(count * sizeof(void *)); 540 if (!values) 541 return false; 542 CFSetGetValues(object, values); 543 } else { 544 values = 0; 545 } 546 547 for (i = 0; ok && (i < count); i++) { 548 ok = DoCFSerialize((CFTypeRef) values[i], state); 549 } 550 551 if (values) 552 free(values); 553 554 return ok && addEndTag(object, state); 555} 556 557static Boolean 558DoCFSerializeDictionary(CFDictionaryRef object, 559 IOCFSerializeState * state) 560{ 561 CFIndex count, i; 562 const void ** values; 563 const void ** keys; 564 Boolean ok = true; 565 566 if (previouslySerialized(object, state)) return true; 567 568 if (!addStartTag(object, 0, state)) return false; 569 570 count = CFDictionaryGetCount(object); 571 572 if (count) { 573 values = (const void **) malloc(2 * count * sizeof(void *)); 574 if (!values) 575 return false; 576 keys = values + count; 577 CFDictionaryGetKeysAndValues(object, keys, values); 578 } else { 579 values = keys = 0; 580 } 581 582 for (i = 0; ok && (i < count); i++) { 583 ok = DoCFSerializeKey((CFTypeRef) keys[i], state); 584 if (!ok) 585 break; 586 if (!(ok = DoCFSerialize((CFTypeRef) values[i], state))) 587 break; 588 } 589 590 if (values) 591 free(values); 592 593 return ok && addEndTag(object, state); 594} 595 596static Boolean 597DoIdrefScan(CFTypeRef object, IOCFSerializeState * state) 598{ 599 CFTypeID type; 600 Boolean ok = true; // such an optimist 601 CFIndex count, i; 602 const void ** keys; 603 const void ** values; 604 605 assert(object); 606 607 recordObjectInIDRefDictionary(object, state); 608 609 type = CFGetTypeID(object); 610 611 if (type == CFDictionaryGetTypeID()) { 612 CFDictionaryRef dict = (CFDictionaryRef)object; 613 count = CFDictionaryGetCount(dict); 614 if (count) { 615 keys = (const void **)malloc(count * sizeof(void *)); 616 values = (const void **)malloc(count * sizeof(void *)); 617 if (!keys || !values) { 618 return false; 619 } 620 CFDictionaryGetKeysAndValues(dict, keys, values); 621 for (i = 0; ok && (i < count); i++) { 622 // don't record dictionary keys 623 ok &= DoIdrefScan((CFTypeRef)values[i], state); 624 } 625 626 free(keys); 627 free(values); 628 if (!ok) { 629 return ok; 630 } 631 } 632 } else if (type == CFArrayGetTypeID()) { 633 CFArrayRef array = (CFArrayRef)object; 634 count = CFArrayGetCount(array); 635 for (i = 0; i < count; i++) { 636 CFTypeRef element = CFArrayGetValueAtIndex(array, i); 637 ok = DoIdrefScan(element, state); 638 if (!ok) { 639 return ok; 640 } 641 } 642 } else if (type == CFSetGetTypeID()) { 643 CFSetRef set = (CFSetRef)object; 644 count = CFSetGetCount(set); 645 if (count) { 646 values = (const void **)malloc(count * sizeof(void *)); 647 if (!values) { 648 return false; 649 } 650 CFSetGetValues(set, values); 651 for (i = 0; ok && (i < count); i++) { 652 ok = DoIdrefScan((CFTypeRef)values[i], state); 653 } 654 655 free(values); 656 if (!ok) { 657 return ok; 658 } 659 } 660 661 } 662 663 return ok; 664} 665 666static Boolean 667DoCFSerialize(CFTypeRef object, IOCFSerializeState * state) 668{ 669 CFTypeID type; 670 Boolean ok; 671 672 assert(object); 673 674 type = CFGetTypeID(object); 675 676 /* Sorted by rough order of % occurrence in big plists. 677 */ 678 if (type == CFDictionaryGetTypeID()) { 679 ok = DoCFSerializeDictionary((CFDictionaryRef) object, state); 680 } else if (type == CFStringGetTypeID()) { 681 ok = DoCFSerializeString((CFStringRef) object, state); 682 } else if (type == CFArrayGetTypeID()) { 683 ok = DoCFSerializeArray((CFArrayRef) object, state); 684 } else if (type == CFNumberGetTypeID()) { 685 ok = DoCFSerializeNumber((CFNumberRef) object, state); 686 } else if (type == CFDataGetTypeID()) { 687 ok = DoCFSerializeData((CFDataRef) object, state); 688 } else if (type == CFBooleanGetTypeID()) { 689 ok = DoCFSerializeBoolean((CFBooleanRef) object, state); 690 } else if (type == CFSetGetTypeID()) { 691 ok = DoCFSerializeSet((CFSetRef) object, state); 692 } else { 693 CFStringRef temp = NULL; 694 temp = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, 695 CFSTR("<string>typeID 0x%x not serializable</string>"), (int) type); 696 if (temp) { 697 ok = DoCFSerializeString(temp, state); 698 CFRelease(temp); 699 } else { 700 ok = false; 701 } 702 703 } 704 705 return ok; 706} 707 708CFDataRef 709IOCFSerialize(CFTypeRef object, CFOptionFlags options) 710{ 711 IOCFSerializeState state; 712 Boolean ok = FALSE; 713 CFDictionaryKeyCallBacks idrefKeyCallbacks; 714 715 if (!object) return 0; 716#if IOKIT_SERVER_VERSION >= 20140421 717 if (kIOCFSerializeToBinary & options) return IOCFSerializeBinary(object, options); 718#endif /* IOKIT_SERVER_VERSION >= 20140421 */ 719 if (options) return 0; 720 721 state.data = CFDataCreateMutable(kCFAllocatorDefault, 0); 722 assert(state.data); 723 724 state.idrefNumRefs = 0; 725 726 idrefKeyCallbacks = kCFTypeDictionaryKeyCallBacks; 727 // only use pointer equality for these keys 728 idrefKeyCallbacks.equal = NULL; 729 730 state.stringIDRefDictionary = CFDictionaryCreateMutable( 731 kCFAllocatorDefault, 0, 732 &idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks); 733 assert(state.stringIDRefDictionary); 734 735 state.numberIDRefDictionary = CFDictionaryCreateMutable( 736 kCFAllocatorDefault, 0, 737 &idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks); 738 assert(state.numberIDRefDictionary); 739 740 state.dataIDRefDictionary = CFDictionaryCreateMutable( 741 kCFAllocatorDefault, 0, 742 &idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks); 743 assert(state.dataIDRefDictionary); 744 745 state.dictionaryIDRefDictionary = CFDictionaryCreateMutable( 746 kCFAllocatorDefault, 0, 747 &idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks); 748 assert(state.dictionaryIDRefDictionary); 749 750 state.arrayIDRefDictionary = CFDictionaryCreateMutable( 751 kCFAllocatorDefault, 0, 752 &idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks); 753 assert(state.arrayIDRefDictionary); 754 755 state.setIDRefDictionary = CFDictionaryCreateMutable( 756 kCFAllocatorDefault, 0, 757 &idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks); 758 assert(state.setIDRefDictionary); 759 760 ok = DoIdrefScan(object, &state); 761 if (!ok) { 762 goto finish; 763 } 764 765 ok = DoCFSerialize(object, &state); 766 767 if (ok) { 768 ok = addChar(0, &state); 769 } 770 if (!ok) { 771 goto finish; 772 } 773 774finish: 775 if (!ok && state.data) { 776 CFRelease(state.data); 777 state.data = NULL; // it's returned 778 } 779 780 if (state.stringIDRefDictionary) CFRelease(state.stringIDRefDictionary); 781 if (state.numberIDRefDictionary) CFRelease(state.numberIDRefDictionary); 782 if (state.dataIDRefDictionary) CFRelease(state.dataIDRefDictionary); 783 if (state.dictionaryIDRefDictionary) CFRelease(state.dictionaryIDRefDictionary); 784 if (state.arrayIDRefDictionary) CFRelease(state.arrayIDRefDictionary); 785 if (state.setIDRefDictionary) CFRelease(state.setIDRefDictionary); 786 787 return state.data; 788} 789 790/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 791 792#if IOKIT_SERVER_VERSION >= 20140421 793 794/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 795 796#if 0 797#define DEBG(fmt, args...) { printf(fmt, args); } 798#else 799#define DEBG(fmt, args...) {} 800#endif 801 802/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 803 804struct IOCFSerializeBinaryState 805{ 806 CFMutableDataRef data; 807 CFMutableDictionaryRef tags; 808 Boolean endCollection; 809 uintptr_t tag; 810}; 811typedef struct IOCFSerializeBinaryState IOCFSerializeBinaryState; 812 813static Boolean 814IOCFSerializeBinaryAdd(IOCFSerializeBinaryState * state, const void * bits, size_t size) 815{ 816 CFDataAppendBytes(state->data, bits, size); 817 if (3 & size) CFDataIncreaseLength(state->data, 4 - (3 & size)); 818 return true; 819} 820 821static Boolean 822IOCFSerializeBinaryAddObject(IOCFSerializeBinaryState * state, 823 CFTypeRef o, uint32_t key, 824 const void * bits, size_t size, size_t zero) 825{ 826 // add to tag dictionary 827 CFDictionarySetValue(state->tags, o, (const void *)state->tag); 828 state->tag++; 829 830 if (state->endCollection) 831 { 832 state->endCollection = false; 833 key |= kOSSerializeEndCollecton; 834 } 835 836 CFDataAppendBytes(state->data, (const UInt8 *) &key, sizeof(key)); 837 CFDataAppendBytes(state->data, bits, size - zero); 838 if (zero) CFDataIncreaseLength(state->data, zero); 839 if (3 & size) CFDataIncreaseLength(state->data, 4 - (3 & size)); 840 841 return (true); 842} 843 844struct ApplierState 845{ 846 IOCFSerializeBinaryState * state; 847 CFIndex index; 848 CFIndex count; 849 Boolean ok; 850}; 851typedef struct ApplierState ApplierState; 852 853static Boolean 854DoCFSerializeBinary(IOCFSerializeBinaryState * state, CFTypeRef o, Boolean isKey); 855 856static void 857IOCFSerializeBinaryCFDictionaryFunction(const void *key, const void *value, void *context) 858{ 859 ApplierState * ctx = (typeof(ctx)) context; 860 Boolean ok; 861 862 ctx->index++; 863 ok = DoCFSerializeBinary(ctx->state, key, true); 864 assert(ok); 865 ctx->state->endCollection = (ctx->index == ctx->count); 866 ok = DoCFSerializeBinary(ctx->state, value, false); 867} 868 869static void 870IOCFSerializeBinaryCFArraySetFunction(const void *value, void *context) 871{ 872 ApplierState * ctx = (typeof(ctx)) context; 873 874 ctx->index++; 875 ctx->state->endCollection = (ctx->index == ctx->count); 876 ctx->ok &= DoCFSerializeBinary(ctx->state, value, false); 877} 878 879static Boolean 880DoCFSerializeBinary(IOCFSerializeBinaryState * state, CFTypeRef o, Boolean isKey) 881{ 882 ApplierState applierState; 883 Boolean ok; 884 CFTypeID type; 885 CFIndex count; 886 uint32_t key; 887 size_t len; 888 uintptr_t tag; 889 890 // look it up 891 tag = (uintptr_t) CFDictionaryGetValue(state->tags, o); 892 // does it exist? 893 if (tag) 894 { 895 key = (kOSSerializeObject | (tag & kOSSerializeDataMask)); 896 if (state->endCollection) 897 { 898 state->endCollection = false; 899 key |= kOSSerializeEndCollecton; 900 } 901 ok = IOCFSerializeBinaryAdd(state, &key, sizeof(key)); 902 return (ok); 903 } 904 905 type = CFGetTypeID(o); 906 applierState.state = state; 907 908 if (type == CFDictionaryGetTypeID()) 909 { 910 count = CFDictionaryGetCount(o); 911 key = (kOSSerializeDictionary | count); 912 ok = IOCFSerializeBinaryAddObject(state, o, key, NULL, 0, 0); 913 if (ok) 914 { 915 applierState.ok = true; 916 applierState.index = 0; 917 applierState.count = count; 918 CFDictionaryApplyFunction(o, &IOCFSerializeBinaryCFDictionaryFunction, &applierState); 919 ok = applierState.ok; 920 } 921 } 922 else if (type == CFArrayGetTypeID()) 923 { 924 count = CFArrayGetCount(o); 925 key = (kOSSerializeArray | count); 926 ok = IOCFSerializeBinaryAddObject(state, o, key, NULL, 0, 0); 927 if (ok) 928 { 929 applierState.index = 0; 930 applierState.count = count; 931 CFArrayApplyFunction(o, CFRangeMake(0, count), &IOCFSerializeBinaryCFArraySetFunction, &applierState); 932 ok = applierState.ok; 933 } 934 } 935 else if (type == CFSetGetTypeID()) 936 { 937 count = CFArrayGetCount(o); 938 key = (kOSSerializeSet | count); 939 ok = IOCFSerializeBinaryAddObject(state, o, key, NULL, 0, 0); 940 if (ok) 941 { 942 applierState.index = 0; 943 applierState.count = count; 944 CFSetApplyFunction(o, &IOCFSerializeBinaryCFArraySetFunction, &applierState); 945 ok = applierState.ok; 946 } 947 } 948 else if (type == CFNumberGetTypeID()) 949 { 950 long long value; 951 int size; 952 953 ok = CFNumberGetValue(o, kCFNumberLongLongType, &value); 954 if (ok) 955 { 956 switch(CFNumberGetType(o)) 957 { 958 case kCFNumberSInt8Type: 959 case kCFNumberCharType: 960 size = sizeof(SInt8); 961 break; 962 963 case kCFNumberSInt16Type: 964 case kCFNumberShortType: 965 size = sizeof(SInt16); 966 break; 967 968 case kCFNumberSInt32Type: 969 case kCFNumberIntType: 970 size = sizeof(SInt32); 971 break; 972 973 case kCFNumberLongType: 974 size = sizeof(long); 975 break; 976 977 case kCFNumberSInt64Type: 978 case kCFNumberLongLongType: 979 default: 980 size = sizeof(SInt64); 981 break; 982 } 983 key = (kOSSerializeNumber | (size * 8)); 984 ok = IOCFSerializeBinaryAddObject(state, o, key, &value, sizeof(value), 0); 985 } 986 } 987 else if (type == CFBooleanGetTypeID()) 988 { 989 key = (kOSSerializeBoolean | (kCFBooleanTrue == o)); 990 ok = IOCFSerializeBinaryAddObject(state, o, key, NULL, 0, 0); 991 } 992 else if (type == CFStringGetTypeID()) 993 { 994 CFDataRef dataBuffer = 0; 995 const char * buffer; 996 bool conversionFailed = false; 997 998 if ((buffer = CFStringGetCStringPtr(o, kCFStringEncodingUTF8))) len = CFStringGetLength(o); 999 else 1000 { 1001 dataBuffer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, o, kCFStringEncodingUTF8, 0); 1002 if (!dataBuffer) 1003 { 1004 dataBuffer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, o, kCFStringEncodingUTF8, (UInt8)'?'); 1005 conversionFailed = true; 1006 } 1007 1008 if (dataBuffer) 1009 { 1010 len = CFDataGetLength(dataBuffer); 1011 buffer = (char *) CFDataGetBytePtr(dataBuffer); 1012 } 1013 else 1014 { 1015 len = 0; 1016 buffer = ""; 1017 conversionFailed = true; 1018 } 1019 } 1020 1021 if (conversionFailed) 1022 { 1023 char * tempBuffer; 1024 if (buffer && (tempBuffer = malloc(len + 1))) 1025 { 1026 bcopy(buffer, tempBuffer, len); 1027 tempBuffer[len] = 0; 1028 syslog(LOG_ERR, "FIXME: IOCFSerialize has detected a string that can not be converted to UTF-8, \"%s\"", tempBuffer); 1029 free(tempBuffer); 1030 } 1031 } 1032 1033 if (isKey) 1034 { 1035 len++; 1036 key = (kOSSerializeSymbol | len); 1037 ok = IOCFSerializeBinaryAddObject(state, o, key, buffer, len, 1); 1038 } 1039 else 1040 { 1041 key = (kOSSerializeString | len); 1042 ok = IOCFSerializeBinaryAddObject(state, o, key, buffer, len, 0); 1043 } 1044 1045 if (dataBuffer) CFRelease(dataBuffer); 1046 } 1047 else if (type == CFDataGetTypeID()) 1048 { 1049 len = CFDataGetLength(o); 1050 key = (kOSSerializeData | len); 1051 ok = IOCFSerializeBinaryAddObject(state, o, key, CFDataGetBytePtr(o), len, 0); 1052 } 1053 else 1054 { 1055 CFStringRef temp; 1056 temp = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, 1057 CFSTR("<string>typeID 0x%x not serializable</string>"), (int) type); 1058 if ((ok = (NULL != temp))) 1059 { 1060 ok = DoCFSerializeBinary(state, temp, false); 1061 CFRelease(temp); 1062 } 1063 } 1064 1065 return (ok); 1066} 1067 1068CFDataRef 1069IOCFSerializeBinary(CFTypeRef object, CFOptionFlags options __unused) 1070{ 1071 Boolean ok; 1072 IOCFSerializeBinaryState state; 1073 1074 bzero(&state, sizeof(state)); 1075 1076 state.endCollection = true; 1077 state.data = CFDataCreateMutable(kCFAllocatorDefault, 0); 1078 assert(state.data); 1079 1080 CFDictionaryKeyCallBacks keyCallbacks; 1081 keyCallbacks = kCFTypeDictionaryKeyCallBacks; 1082 // only use pointer equality for these keys 1083 keyCallbacks.equal = NULL; 1084 1085 state.tags = CFDictionaryCreateMutable( 1086 kCFAllocatorDefault, 0, 1087 &keyCallbacks, 1088 (CFDictionaryValueCallBacks *) NULL); 1089 1090 assert(state.tags); 1091 1092 IOCFSerializeBinaryAdd(&state, kOSSerializeBinarySignature, sizeof(kOSSerializeBinarySignature)); 1093 1094 ok = DoCFSerializeBinary(&state, object, false); 1095 1096 if (!ok && state.data) 1097 { 1098 CFRelease(state.data); 1099 state.data = NULL; // it's returned 1100 } 1101 if (state.tags) CFRelease(state.tags); 1102 1103 return (state.data); 1104} 1105 1106/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 1107 1108#define setAtIndex(v, idx, o) \ 1109 if (idx >= v##Capacity) \ 1110 { \ 1111 uint32_t ncap = v##Capacity + 64; \ 1112 typeof(v##Array) nbuf = (typeof(v##Array)) malloc(ncap * sizeof(o)); \ 1113 if (!nbuf) ok = false; \ 1114 if (v##Array) \ 1115 { \ 1116 bcopy(v##Array, nbuf, v##Capacity * sizeof(o)); \ 1117 free(v##Array); \ 1118 } \ 1119 v##Array = nbuf; \ 1120 v##Capacity = ncap; \ 1121 } \ 1122 if (ok) v##Array[idx] = o; 1123 1124 1125/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 1126 1127CFTypeRef 1128IOCFUnserializeBinary(const char * buffer, 1129 size_t bufferSize, 1130 CFAllocatorRef allocator, 1131 CFOptionFlags options __unused, 1132 CFStringRef * errorString) 1133{ 1134 CFTypeRef * objsArray; 1135 uint32_t objsCapacity; 1136 uint32_t objsIdx; 1137 1138 CFTypeRef * stackArray; 1139 uint32_t stackCapacity; 1140 uint32_t stackIdx; 1141 1142 CFTypeRef result; 1143 CFTypeRef parent; 1144 CFMutableDictionaryRef dict; 1145 CFMutableArrayRef array; 1146 CFMutableSetRef set; 1147 CFMutableDictionaryRef newDict; 1148 CFMutableArrayRef newArray; 1149 CFMutableSetRef newSet; 1150 CFTypeRef o; 1151 CFStringRef sym; 1152 1153 size_t bufferPos; 1154 const uint32_t * next; 1155 uint32_t key, len, wordLen; 1156 bool ok, end, newCollect, isRef; 1157 1158 CFNumberType numType; 1159 CFTypeID type; 1160 const UInt8 * bytes; 1161 1162 if (errorString) *errorString = NULL; 1163 if (0 != strcmp(kOSSerializeBinarySignature, buffer)) return (NULL); 1164 if (3 & ((uintptr_t) buffer)) return (NULL); 1165 if (bufferSize < sizeof(kOSSerializeBinarySignature)) return (NULL); 1166 bufferPos = sizeof(kOSSerializeBinarySignature); 1167 next = (typeof(next)) (((uintptr_t) buffer) + sizeof(kOSSerializeBinarySignature)); 1168 1169 DEBG("---------OSUnserializeBinary(%p)\n", buffer); 1170 1171 objsArray = stackArray = NULL; 1172 objsIdx = objsCapacity = 0; 1173 stackIdx = stackCapacity = 0; 1174 1175 result = 0; 1176 parent = 0; 1177 dict = 0; 1178 array = 0; 1179 set = 0; 1180 sym = 0; 1181 1182 ok = true; 1183 while (ok) 1184 { 1185 bufferPos += sizeof(*next); 1186 if (!(ok = (bufferPos <= bufferSize))) break; 1187 key = *next++; 1188 1189 len = (key & kOSSerializeDataMask); 1190 wordLen = (len + 3) >> 2; 1191 end = (0 != (kOSSerializeEndCollecton & key)); 1192 DEBG("key 0x%08x: 0x%04x, %d\n", key, len, end); 1193 1194 newCollect = isRef = false; 1195 o = 0; newDict = 0; newArray = 0; newSet = 0; 1196 1197 switch (kOSSerializeTypeMask & key) 1198 { 1199 case kOSSerializeDictionary: 1200 o = newDict = CFDictionaryCreateMutable(allocator, len, 1201 &kCFTypeDictionaryKeyCallBacks, 1202 &kCFTypeDictionaryValueCallBacks); 1203 newCollect = (len != 0); 1204 break; 1205 case kOSSerializeArray: 1206 o = newArray = CFArrayCreateMutable(allocator, len, &kCFTypeArrayCallBacks); 1207 newCollect = (len != 0); 1208 break; 1209 case kOSSerializeSet: 1210 o = newSet = CFSetCreateMutable(allocator, len, &kCFTypeSetCallBacks); 1211 newCollect = (len != 0); 1212 break; 1213 1214 case kOSSerializeObject: 1215 if (len >= objsIdx) break; 1216 o = objsArray[len]; 1217 CFRetain(o); 1218 isRef = true; 1219 break; 1220 1221 case kOSSerializeNumber: 1222 bufferPos += sizeof(long long); 1223 if (bufferPos > bufferSize) break; 1224 bytes = (const UInt8 *) &next[0]; 1225 if (len <= 32) { 1226 numType = kCFNumberSInt32Type; 1227 } else { 1228 numType = kCFNumberSInt64Type; 1229 } 1230 o = CFNumberCreate(allocator, numType, (const void *) bytes); 1231 next += 2; 1232 break; 1233 1234 case kOSSerializeSymbol: 1235 len--; 1236 /* fall thru */ 1237 case kOSSerializeString: 1238 bufferPos += (wordLen * sizeof(uint32_t)); 1239 if (bufferPos > bufferSize) break; 1240 o = CFStringCreateWithBytes(allocator, (const UInt8 *) next, len, kCFStringEncodingUTF8, false); 1241 if (!o) 1242 { 1243 o = CFStringCreateWithBytes(allocator, (const UInt8 *) next, len, kCFStringEncodingMacRoman, false); 1244 syslog(LOG_ERR, "FIXME: IOUnserialize has detected a string that is not valid UTF-8, \"%s\".", 1245 CFStringGetCStringPtr(o, kCFStringEncodingMacRoman)); 1246 } 1247 next += wordLen; 1248 break; 1249 1250 case kOSSerializeData: 1251 bufferPos += (wordLen * sizeof(uint32_t)); 1252 if (bufferPos > bufferSize) break; 1253 o = CFDataCreate(allocator, (const UInt8 *) next, len); 1254 next += wordLen; 1255 break; 1256 1257 case kOSSerializeBoolean: 1258 o = (len ? kCFBooleanTrue : kCFBooleanFalse); 1259 CFRetain(o); 1260 break; 1261 1262 default: 1263 break; 1264 } 1265 1266 if (!(ok = (o != 0))) break; 1267 1268 if (!isRef) 1269 { 1270 setAtIndex(objs, objsIdx, o); 1271 if (!ok) break; 1272 objsIdx++; 1273 } 1274 if (dict) 1275 { 1276 if (sym) 1277 { 1278 if (o != dict) CFDictionarySetValue(dict, sym, o); 1279 CFRelease(o); 1280 CFRelease(sym); 1281 sym = 0; 1282 } 1283 else 1284 { 1285 assert(CFStringGetTypeID() == CFGetTypeID(o)); 1286 sym = o; 1287 } 1288 } 1289 else if (array) 1290 { 1291 CFArrayAppendValue(array, o); 1292 CFRelease(o); 1293 } 1294 else if (set) 1295 { 1296 CFSetAddValue(set, o); 1297 CFRelease(o); 1298 } 1299 else 1300 { 1301 assert(!parent); 1302 result = o; 1303 } 1304 1305 if (!ok) break; 1306 1307 if (newCollect) 1308 { 1309 if (!end) 1310 { 1311 stackIdx++; 1312 setAtIndex(stack, stackIdx, parent); 1313 if (!ok) break; 1314 } 1315 DEBG("++stack[%d] %p\n", stackIdx, parent); 1316 parent = o; 1317 dict = newDict; 1318 array = newArray; 1319 set = newSet; 1320 end = false; 1321 } 1322 1323 if (end) 1324 { 1325 if (!stackIdx) break; 1326 parent = stackArray[stackIdx]; 1327 DEBG("--stack[%d] %p\n", stackIdx, parent); 1328 stackIdx--; 1329 1330 type = CFGetTypeID(parent); 1331 set = NULL; 1332 dict = NULL; 1333 array = NULL; 1334 if (type == CFDictionaryGetTypeID()) dict = (CFMutableDictionaryRef) parent; 1335 else if (type == CFArrayGetTypeID()) array = (CFMutableArrayRef) parent; 1336 else if (type == CFSetGetTypeID()) set = (CFMutableSetRef) parent; 1337 else ok = false; 1338 } 1339 } 1340 DEBG("ret %p\n", result); 1341 1342 if (objsCapacity) free(objsArray); 1343 if (stackCapacity) free(stackArray); 1344 1345 if (!ok && result) 1346 { 1347 CFRelease(result); 1348 result = 0; 1349 } 1350 return (result); 1351} 1352 1353/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 1354 1355#endif /* IOKIT_SERVER_VERSION >= 20140421 */ 1356 1357/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 1358 1359CFTypeRef 1360IOCFUnserializeWithSize(const char * buffer, 1361 size_t bufferSize, 1362 CFAllocatorRef allocator, 1363 CFOptionFlags options, 1364 CFStringRef * errorString) 1365{ 1366 if (errorString) *errorString = NULL; 1367 if (!buffer) return 0; 1368 1369#if IOKIT_SERVER_VERSION >= 20140421 1370 if (bufferSize < sizeof(kOSSerializeBinarySignature)) return (0); 1371 if ((kIOCFSerializeToBinary & options) 1372 || (!strcmp(kOSSerializeBinarySignature, buffer))) return (IOCFUnserializeBinary(buffer, bufferSize, allocator, options, errorString)); 1373#else 1374 if (!bufferSize) return (0); 1375#endif /* IOKIT_SERVER_VERSION >= 20140421 */ 1376 1377 return (IOCFUnserialize(buffer, allocator, options, errorString)); 1378} 1379