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("&lt;", state);
380                break;
381            case '>':
382                succeeded = addString("&gt;", state);
383                break;
384            case '&':
385                succeeded = addString("&amp;", 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("&lt;", state);
426                break;
427            case '>':
428                succeeded = addString("&gt;", state);
429                break;
430            case '&':
431                succeeded = addString("&amp;", 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