#include <device/device_types.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOCFSerialize.h>
#include <IOKit/IOCFUnserialize.h>
#if IOKIT_SERVER_VERSION >= 20140421
#include <System/libkern/OSSerializeBinary.h>
#endif
#include <assert.h>
#include <syslog.h>
typedef struct {
CFMutableDataRef data;
int idrefNumRefs;
CFMutableDictionaryRef stringIDRefDictionary;
CFMutableDictionaryRef numberIDRefDictionary;
CFMutableDictionaryRef dataIDRefDictionary;
CFMutableDictionaryRef dictionaryIDRefDictionary;
CFMutableDictionaryRef arrayIDRefDictionary;
CFMutableDictionaryRef setIDRefDictionary;
} IOCFSerializeState;
static Boolean
DoCFSerialize(CFTypeRef object, IOCFSerializeState * state);
static CFDataRef
IOCFSerializeBinary(CFTypeRef object, CFOptionFlags options);
static Boolean
addChar(char chr, IOCFSerializeState * state)
{
CFDataAppendBytes(state->data, (UInt8 *) &chr, 1);
return true;
}
static Boolean
addString(const char * str, IOCFSerializeState * state)
{
CFIndex len = strlen(str);
CFDataAppendBytes(state->data, (UInt8 *) str, len);
return true;
}
static const char *
getTagString(CFTypeRef object)
{
CFTypeID type;
assert(object);
type = CFGetTypeID(object);
if (type == CFStringGetTypeID()) return "string";
if (type == CFNumberGetTypeID()) return "integer";
if (type == CFDataGetTypeID()) return "data";
if (type == CFDictionaryGetTypeID()) return "dict";
if (type == CFArrayGetTypeID()) return "array";
if (type == CFSetGetTypeID()) return "set";
return "internal error";
}
static CFMutableDictionaryRef
idRefDictionaryForObject(
CFTypeRef object,
IOCFSerializeState * state)
{
CFMutableDictionaryRef result = NULL;
CFTypeID objectType = CFNullGetTypeID();
objectType = CFGetTypeID(object);
if (objectType == CFDictionaryGetTypeID()) {
result = state->dictionaryIDRefDictionary;
} else if (objectType == CFStringGetTypeID()) {
result = state->stringIDRefDictionary;
} else if (objectType == CFArrayGetTypeID()) {
result = state->arrayIDRefDictionary;
} else if (objectType == CFNumberGetTypeID()) {
result = state->numberIDRefDictionary;
} else if (objectType == CFDataGetTypeID()) {
result = state->dataIDRefDictionary;
} else if (objectType == CFSetGetTypeID()) {
result = state->setIDRefDictionary;
}
return result;
}
void
recordObjectInIDRefDictionary(
CFTypeRef object,
IOCFSerializeState * state)
{
CFTypeRef refEntry = NULL; CFMutableDictionaryRef idRefDictionary = NULL;
if (!object || !state) {
goto finish;
}
idRefDictionary = idRefDictionaryForObject(object, state);
if (!idRefDictionary) {
goto finish;
}
refEntry = CFDictionaryGetValue(idRefDictionary, object);
if (!refEntry) {
CFDictionaryAddValue(idRefDictionary, object, kCFBooleanFalse);
} else {
CFDictionarySetValue(idRefDictionary, object, kCFBooleanTrue);
}
finish:
return;
}
Boolean
previouslySerialized(
CFTypeRef object,
IOCFSerializeState * state)
{
Boolean result = FALSE;
CFMutableDictionaryRef idRefDict = NULL; CFTypeRef idRefEntry = NULL; CFTypeID idRefType = CFNullGetTypeID(); char temp[64];
int idInt = -1;
if (!object || !state) {
goto finish;
}
idRefDict = idRefDictionaryForObject(object, state);
if (!idRefDict) {
goto finish;
}
idRefEntry = (CFTypeRef)CFDictionaryGetValue(idRefDict, object);
if (!idRefEntry) {
goto finish;
}
idRefType = CFGetTypeID(idRefEntry);
if (idRefType == CFBooleanGetTypeID()) {
goto finish;
}
if (idRefType != CFNumberGetTypeID()) {
goto finish;
}
if (!CFNumberGetValue((CFNumberRef)idRefEntry, kCFNumberIntType, &idInt)) {
goto finish;
}
snprintf(temp, sizeof(temp), "<%s IDREF=\"%d\"/>", getTagString(object), idInt);
result = addString(temp, state);
finish:
return result;
}
static Boolean
addStartTag(
CFTypeRef object,
const char * additionalTags,
IOCFSerializeState * state)
{
CFMutableDictionaryRef idRefDict = NULL; CFTypeRef idRefEntry = NULL; CFNumberRef idRef = NULL; char temp[128];
idRefDict = idRefDictionaryForObject(object, state);
if (idRefDict) {
idRefEntry = (CFTypeRef)CFDictionaryGetValue(idRefDict, object);
}
if (idRefEntry == kCFBooleanTrue) {
int idInt = state->idrefNumRefs++;
idRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &idInt);
assert(idRef);
CFDictionarySetValue(idRefDict, object, idRef);
CFRelease(idRef);
if (additionalTags) {
snprintf(temp, sizeof(temp) * sizeof(char),
"<%s ID=\"%d\" %s>", getTagString(object), idInt, additionalTags);
} else {
snprintf(temp, sizeof(temp) * sizeof(char),
"<%s ID=\"%d\">", getTagString(object), idInt);
}
} else {
if (additionalTags) {
snprintf(temp, sizeof(temp) * sizeof(char),
"<%s %s>", getTagString(object), additionalTags);
} else {
snprintf(temp, sizeof(temp) * sizeof(char),
"<%s>", getTagString(object));
}
}
return addString(temp, state);
}
static Boolean
addEndTag(CFTypeRef object,
IOCFSerializeState * state)
{
char temp[128];
snprintf(temp, sizeof(temp) * sizeof(char), "</%s>", getTagString(object));
return addString(temp, state);
}
static Boolean
DoCFSerializeNumber(CFNumberRef object, IOCFSerializeState * state)
{
char temp[32];
long long value;
int size;
if (previouslySerialized(object, state)) return true;
if (!CFNumberGetValue(object, kCFNumberLongLongType, &value)) {
return(false);
}
switch(CFNumberGetType(object)) {
case kCFNumberSInt8Type:
case kCFNumberCharType:
size = 8;
break;
case kCFNumberSInt16Type:
case kCFNumberShortType:
size = 16;
break;
case kCFNumberSInt32Type:
case kCFNumberIntType:
size = 32;
break;
case kCFNumberLongType:
#if __LP64__
size = 64;
#else
size = 32;
#endif
break;
case kCFNumberSInt64Type:
case kCFNumberLongLongType:
default:
size = 64;
break;
}
snprintf(temp, sizeof(temp), "size=\"%d\"", size);
if (!addStartTag(object, temp, state)) return false;
if (size <= 32)
snprintf(temp, sizeof(temp), "0x%lx", (unsigned long int) value);
else
snprintf(temp, sizeof(temp), "0x%qx", value);
if (!addString(temp, state)) return false;
return addEndTag(object, state);
}
static Boolean
DoCFSerializeBoolean(CFBooleanRef object, IOCFSerializeState * state)
{
return addString(CFBooleanGetValue(object) ? "<true/>" : "<false/>", state);
}
static Boolean
DoCFSerializeString(CFStringRef object, IOCFSerializeState * state)
{
CFDataRef dataBuffer = 0;
const char *buffer = "";
CFIndex length = 0;
char c;
int i;
Boolean succeeded = true;
if (previouslySerialized(object, state)) return true;
if (!addStartTag(object, 0, state)) return false;
dataBuffer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, object, kCFStringEncodingUTF8, '?');
if (dataBuffer) {
length = CFDataGetLength(dataBuffer);
buffer = (char *) CFDataGetBytePtr(dataBuffer);
}
for (i = 0; (i < length) && succeeded; i++) {
c = buffer[i];
switch (c) {
case '<':
succeeded = addString("<", state);
break;
case '>':
succeeded = addString(">", state);
break;
case '&':
succeeded = addString("&", state);
break;
default:
succeeded = addChar(c, state);
break;
}
}
if (dataBuffer) CFRelease(dataBuffer);
if (succeeded)
return addEndTag(object, state);
else
return false;
}
static Boolean
DoCFSerializeKey(CFStringRef object, IOCFSerializeState * state)
{
CFDataRef dataBuffer = 0;
const char *buffer = "";
CFIndex length = 0;
char c;
int i;
Boolean succeeded = true;
const char *getOffMyXMLawn = "<!-- \xf0\x9f\xa4\xa6 -->";
const char *classNames[] = { "AppleLSIFusionFC", "AppleLSIFusionSAS", "AppleLSIFusionSCSI",
"AppleUSBXHCI", "ICH8 ATA/100", "Physical Interconnect Location",
"IOPCITunnelCompatible" };
int numClasses = (sizeof(classNames) / sizeof(classNames[0]));
bool matches = false;
if (!addString("<key>", state)) return false;
dataBuffer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, object, kCFStringEncodingUTF8, '?');
if (dataBuffer) {
length = CFDataGetLength(dataBuffer);
buffer = (char *) CFDataGetBytePtr(dataBuffer);
}
for (i = 0; i < numClasses; i++) {
if (!strncmp(buffer, classNames[i], length)) {
matches = true;
break;
}
}
for (i = 0; (i < length) && succeeded; i++) {
c = buffer[i];
switch (c) {
case '<':
succeeded = addString("<", state);
break;
case '>':
succeeded = addString(">", state);
break;
case '&':
succeeded = addString("&", state);
break;
default:
succeeded = addChar(c, state);
break;
}
}
if (dataBuffer) CFRelease(dataBuffer);
if (succeeded) {
succeeded = addString("</key>", state);
if (matches && succeeded) {
return addString(getOffMyXMLawn, state);
} else {
return succeeded;
}
} else {
return false;
}
}
static const char __CFPLDataEncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static Boolean
DoCFSerializeData(CFDataRef object, IOCFSerializeState * state)
{
CFIndex length;
const UInt8 * bytes;
CFIndex i;
const unsigned char *p;
unsigned char c = 0;
if (previouslySerialized(object, state)) return true;
length = CFDataGetLength(object);
bytes = CFDataGetBytePtr(object);
if (!addStartTag(object, 0, state)) return false;
for (i = 0, p = (unsigned char *)bytes; i < length; i++, p++) {
switch (i % 3) {
case 0:
c = __CFPLDataEncodeTable [ ((p[0] >> 2) & 0x3f)];
if (!addChar(c, state)) return false;
break;
case 1:
c = __CFPLDataEncodeTable [ ((((p[-1] << 8) | p[0]) >> 4) & 0x3f)];
if (!addChar(c, state)) return false;
break;
case 2:
c = __CFPLDataEncodeTable [ ((((p[-1] << 8) | p[0]) >> 6) & 0x3f)];
if (!addChar(c, state)) return false;
c = __CFPLDataEncodeTable [ (p[0] & 0x3f)];
if (!addChar(c, state)) return false;
break;
}
}
switch (i % 3) {
case 0:
break;
case 1:
c = __CFPLDataEncodeTable [ ((p[-1] << 4) & 0x30)];
if (!addChar(c, state)) return false;
if (!addChar('=', state)) return false;
if (!addChar('=', state)) return false;
break;
case 2:
c = __CFPLDataEncodeTable [ ((p[-1] << 2) & 0x3c)];
if (!addChar(c, state)) return false;
if (!addChar('=', state)) return false;
break;
}
return addEndTag(object, state);
}
static Boolean
DoCFSerializeArray(CFArrayRef object, IOCFSerializeState * state)
{
CFIndex count, i;
Boolean ok = true;
if (previouslySerialized(object, state)) return true;
if (!addStartTag(object, 0, state)) return false;
count = CFArrayGetCount(object);
if (count) {
for (i = 0; ok && (i < count); i++) {
ok = DoCFSerialize((CFTypeRef) CFArrayGetValueAtIndex(object, i),
state);
}
}
return ok && addEndTag(object, state);
}
static Boolean
DoCFSerializeSet(CFSetRef object, IOCFSerializeState * state)
{
CFIndex count, i;
const void ** values;
Boolean ok = true;
if (previouslySerialized(object, state)) return true;
if (!addStartTag(object, 0, state)) return false;
count = CFSetGetCount(object);
if (count) {
values = (const void **) malloc(count * sizeof(void *));
if (!values)
return false;
CFSetGetValues(object, values);
} else {
values = 0;
}
for (i = 0; ok && (i < count); i++) {
ok = DoCFSerialize((CFTypeRef) values[i], state);
}
if (values)
free(values);
return ok && addEndTag(object, state);
}
static Boolean
DoCFSerializeDictionary(CFDictionaryRef object,
IOCFSerializeState * state)
{
CFIndex count, i;
const void ** values;
const void ** keys;
Boolean ok = true;
if (previouslySerialized(object, state)) return true;
if (!addStartTag(object, 0, state)) return false;
count = CFDictionaryGetCount(object);
if (count) {
values = (const void **) malloc(2 * count * sizeof(void *));
if (!values)
return false;
keys = values + count;
CFDictionaryGetKeysAndValues(object, keys, values);
} else {
values = keys = 0;
}
for (i = 0; ok && (i < count); i++) {
ok = DoCFSerializeKey((CFTypeRef) keys[i], state);
if (!ok)
break;
if (!(ok = DoCFSerialize((CFTypeRef) values[i], state)))
break;
}
if (values)
free(values);
return ok && addEndTag(object, state);
}
static Boolean
DoIdrefScan(CFTypeRef object, IOCFSerializeState * state)
{
CFTypeID type;
Boolean ok = true; CFIndex count, i;
const void ** keys;
const void ** values;
assert(object);
recordObjectInIDRefDictionary(object, state);
type = CFGetTypeID(object);
if (type == CFDictionaryGetTypeID()) {
CFDictionaryRef dict = (CFDictionaryRef)object;
count = CFDictionaryGetCount(dict);
if (count) {
keys = (const void **)malloc(count * sizeof(void *));
values = (const void **)malloc(count * sizeof(void *));
if (!keys || !values) {
return false;
}
CFDictionaryGetKeysAndValues(dict, keys, values);
for (i = 0; ok && (i < count); i++) {
ok &= DoIdrefScan((CFTypeRef)values[i], state);
}
free(keys);
free(values);
if (!ok) {
return ok;
}
}
} else if (type == CFArrayGetTypeID()) {
CFArrayRef array = (CFArrayRef)object;
count = CFArrayGetCount(array);
for (i = 0; i < count; i++) {
CFTypeRef element = CFArrayGetValueAtIndex(array, i);
ok = DoIdrefScan(element, state);
if (!ok) {
return ok;
}
}
} else if (type == CFSetGetTypeID()) {
CFSetRef set = (CFSetRef)object;
count = CFSetGetCount(set);
if (count) {
values = (const void **)malloc(count * sizeof(void *));
if (!values) {
return false;
}
CFSetGetValues(set, values);
for (i = 0; ok && (i < count); i++) {
ok = DoIdrefScan((CFTypeRef)values[i], state);
}
free(values);
if (!ok) {
return ok;
}
}
}
return ok;
}
static Boolean
DoCFSerialize(CFTypeRef object, IOCFSerializeState * state)
{
CFTypeID type;
Boolean ok;
assert(object);
type = CFGetTypeID(object);
if (type == CFDictionaryGetTypeID()) {
ok = DoCFSerializeDictionary((CFDictionaryRef) object, state);
} else if (type == CFStringGetTypeID()) {
ok = DoCFSerializeString((CFStringRef) object, state);
} else if (type == CFArrayGetTypeID()) {
ok = DoCFSerializeArray((CFArrayRef) object, state);
} else if (type == CFNumberGetTypeID()) {
ok = DoCFSerializeNumber((CFNumberRef) object, state);
} else if (type == CFDataGetTypeID()) {
ok = DoCFSerializeData((CFDataRef) object, state);
} else if (type == CFBooleanGetTypeID()) {
ok = DoCFSerializeBoolean((CFBooleanRef) object, state);
} else if (type == CFSetGetTypeID()) {
ok = DoCFSerializeSet((CFSetRef) object, state);
} else {
CFStringRef temp = NULL;
temp = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
CFSTR("<string>typeID 0x%x not serializable</string>"), (int) type);
if (temp) {
ok = DoCFSerializeString(temp, state);
CFRelease(temp);
} else {
ok = false;
}
}
return ok;
}
CFDataRef
IOCFSerialize(CFTypeRef object, CFOptionFlags options)
{
IOCFSerializeState state;
Boolean ok = FALSE;
CFDictionaryKeyCallBacks idrefKeyCallbacks;
if (!object) return 0;
#if IOKIT_SERVER_VERSION >= 20140421
if (kIOCFSerializeToBinary & options) return IOCFSerializeBinary(object, options);
#endif
if (options) return 0;
state.data = CFDataCreateMutable(kCFAllocatorDefault, 0);
assert(state.data);
state.idrefNumRefs = 0;
idrefKeyCallbacks = kCFTypeDictionaryKeyCallBacks;
idrefKeyCallbacks.equal = NULL;
state.stringIDRefDictionary = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks);
assert(state.stringIDRefDictionary);
state.numberIDRefDictionary = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks);
assert(state.numberIDRefDictionary);
state.dataIDRefDictionary = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks);
assert(state.dataIDRefDictionary);
state.dictionaryIDRefDictionary = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks);
assert(state.dictionaryIDRefDictionary);
state.arrayIDRefDictionary = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks);
assert(state.arrayIDRefDictionary);
state.setIDRefDictionary = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&idrefKeyCallbacks, &kCFTypeDictionaryValueCallBacks);
assert(state.setIDRefDictionary);
ok = DoIdrefScan(object, &state);
if (!ok) {
goto finish;
}
ok = DoCFSerialize(object, &state);
if (ok) {
ok = addChar(0, &state);
}
if (!ok) {
goto finish;
}
finish:
if (!ok && state.data) {
CFRelease(state.data);
state.data = NULL; }
if (state.stringIDRefDictionary) CFRelease(state.stringIDRefDictionary);
if (state.numberIDRefDictionary) CFRelease(state.numberIDRefDictionary);
if (state.dataIDRefDictionary) CFRelease(state.dataIDRefDictionary);
if (state.dictionaryIDRefDictionary) CFRelease(state.dictionaryIDRefDictionary);
if (state.arrayIDRefDictionary) CFRelease(state.arrayIDRefDictionary);
if (state.setIDRefDictionary) CFRelease(state.setIDRefDictionary);
return state.data;
}
#if IOKIT_SERVER_VERSION >= 20140421
#if 0
#define DEBG(fmt, args...) { printf(fmt, args); }
#else
#define DEBG(fmt, args...) {}
#endif
struct IOCFSerializeBinaryState
{
CFMutableDataRef data;
CFMutableDictionaryRef tags;
Boolean endCollection;
uintptr_t tag;
};
typedef struct IOCFSerializeBinaryState IOCFSerializeBinaryState;
static Boolean
IOCFSerializeBinaryAdd(IOCFSerializeBinaryState * state, const void * bits, size_t size)
{
CFDataAppendBytes(state->data, bits, size);
if (3 & size) CFDataIncreaseLength(state->data, 4 - (3 & size));
return true;
}
static Boolean
IOCFSerializeBinaryAddObject(IOCFSerializeBinaryState * state,
CFTypeRef o, uint32_t key,
const void * bits, size_t size, size_t zero)
{
CFDictionarySetValue(state->tags, o, (const void *)state->tag);
state->tag++;
if (state->endCollection)
{
state->endCollection = false;
key |= kOSSerializeEndCollecton;
}
CFDataAppendBytes(state->data, (const UInt8 *) &key, sizeof(key));
CFDataAppendBytes(state->data, bits, size - zero);
if (zero) CFDataIncreaseLength(state->data, zero);
if (3 & size) CFDataIncreaseLength(state->data, 4 - (3 & size));
return (true);
}
struct ApplierState
{
IOCFSerializeBinaryState * state;
CFIndex index;
CFIndex count;
Boolean ok;
};
typedef struct ApplierState ApplierState;
static Boolean
DoCFSerializeBinary(IOCFSerializeBinaryState * state, CFTypeRef o, Boolean isKey);
static void
IOCFSerializeBinaryCFDictionaryFunction(const void *key, const void *value, void *context)
{
ApplierState * ctx = (typeof(ctx)) context;
ctx->index++;
ctx->ok &= DoCFSerializeBinary(ctx->state, key, true);
ctx->state->endCollection = (ctx->index == ctx->count);
ctx->ok &= DoCFSerializeBinary(ctx->state, value, false);
}
static void
IOCFSerializeBinaryCFArraySetFunction(const void *value, void *context)
{
ApplierState * ctx = (typeof(ctx)) context;
ctx->index++;
ctx->state->endCollection = (ctx->index == ctx->count);
ctx->ok &= DoCFSerializeBinary(ctx->state, value, false);
}
static Boolean
DoCFSerializeBinary(IOCFSerializeBinaryState * state, CFTypeRef o, Boolean isKey)
{
ApplierState applierState;
Boolean ok;
CFTypeID type;
CFIndex count;
uint32_t key;
size_t len;
uintptr_t tag;
tag = (uintptr_t) CFDictionaryGetValue(state->tags, o);
if (tag)
{
key = (kOSSerializeObject | (tag & kOSSerializeDataMask));
if (state->endCollection)
{
state->endCollection = false;
key |= kOSSerializeEndCollecton;
}
ok = IOCFSerializeBinaryAdd(state, &key, sizeof(key));
return (ok);
}
type = CFGetTypeID(o);
applierState.state = state;
if (type == CFDictionaryGetTypeID())
{
count = CFDictionaryGetCount(o);
key = (kOSSerializeDictionary | count);
ok = IOCFSerializeBinaryAddObject(state, o, key, NULL, 0, 0);
if (ok)
{
applierState.ok = true;
applierState.index = 0;
applierState.count = count;
CFDictionaryApplyFunction(o, &IOCFSerializeBinaryCFDictionaryFunction, &applierState);
ok = applierState.ok;
}
}
else if (type == CFArrayGetTypeID())
{
count = CFArrayGetCount(o);
key = (kOSSerializeArray | count);
ok = IOCFSerializeBinaryAddObject(state, o, key, NULL, 0, 0);
if (ok)
{
applierState.ok = true;
applierState.index = 0;
applierState.count = count;
CFArrayApplyFunction(o, CFRangeMake(0, count), &IOCFSerializeBinaryCFArraySetFunction, &applierState);
ok = applierState.ok;
}
}
else if (type == CFSetGetTypeID())
{
count = CFArrayGetCount(o);
key = (kOSSerializeSet | count);
ok = IOCFSerializeBinaryAddObject(state, o, key, NULL, 0, 0);
if (ok)
{
applierState.ok = true;
applierState.index = 0;
applierState.count = count;
CFSetApplyFunction(o, &IOCFSerializeBinaryCFArraySetFunction, &applierState);
ok = applierState.ok;
}
}
else if (type == CFNumberGetTypeID())
{
long long value;
int size;
ok = CFNumberGetValue(o, kCFNumberLongLongType, &value);
if (ok)
{
switch(CFNumberGetType(o))
{
case kCFNumberSInt8Type:
case kCFNumberCharType:
size = sizeof(SInt8);
break;
case kCFNumberSInt16Type:
case kCFNumberShortType:
size = sizeof(SInt16);
break;
case kCFNumberSInt32Type:
case kCFNumberIntType:
size = sizeof(SInt32);
break;
case kCFNumberLongType:
size = sizeof(long);
break;
case kCFNumberSInt64Type:
case kCFNumberLongLongType:
default:
size = sizeof(SInt64);
break;
}
key = (kOSSerializeNumber | (size * 8));
ok = IOCFSerializeBinaryAddObject(state, o, key, &value, sizeof(value), 0);
}
}
else if (type == CFBooleanGetTypeID())
{
key = (kOSSerializeBoolean | (kCFBooleanTrue == o));
ok = IOCFSerializeBinaryAddObject(state, o, key, NULL, 0, 0);
}
else if (type == CFStringGetTypeID())
{
CFDataRef dataBuffer = 0;
const char * buffer;
bool conversionFailed = false;
if ((buffer = CFStringGetCStringPtr(o, kCFStringEncodingUTF8))) len = CFStringGetLength(o);
else
{
dataBuffer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, o, kCFStringEncodingUTF8, 0);
if (!dataBuffer)
{
dataBuffer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, o, kCFStringEncodingUTF8, (UInt8)'?');
conversionFailed = true;
}
if (dataBuffer)
{
len = CFDataGetLength(dataBuffer);
buffer = (char *) CFDataGetBytePtr(dataBuffer);
}
else
{
len = 0;
buffer = "";
conversionFailed = true;
}
}
if (conversionFailed)
{
char * tempBuffer;
if (buffer && (tempBuffer = malloc(len + 1)))
{
bcopy(buffer, tempBuffer, len);
tempBuffer[len] = 0;
syslog(LOG_ERR, "FIXME: IOCFSerialize has detected a string that can not be converted to UTF-8, \"%s\"", tempBuffer);
free(tempBuffer);
}
}
if (isKey)
{
len++;
key = (kOSSerializeSymbol | len);
ok = IOCFSerializeBinaryAddObject(state, o, key, buffer, len, 1);
}
else
{
key = (kOSSerializeString | len);
ok = IOCFSerializeBinaryAddObject(state, o, key, buffer, len, 0);
}
if (dataBuffer) CFRelease(dataBuffer);
}
else if (type == CFDataGetTypeID())
{
len = CFDataGetLength(o);
key = (kOSSerializeData | len);
ok = IOCFSerializeBinaryAddObject(state, o, key, CFDataGetBytePtr(o), len, 0);
}
else
{
CFStringRef temp;
temp = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
CFSTR("<string>typeID 0x%x not serializable</string>"), (int) type);
if ((ok = (NULL != temp)))
{
ok = DoCFSerializeBinary(state, temp, false);
CFRelease(temp);
}
}
return (ok);
}
CFDataRef
IOCFSerializeBinary(CFTypeRef object, CFOptionFlags options __unused)
{
Boolean ok;
IOCFSerializeBinaryState state;
bzero(&state, sizeof(state));
state.endCollection = true;
state.data = CFDataCreateMutable(kCFAllocatorDefault, 0);
assert(state.data);
CFDictionaryKeyCallBacks keyCallbacks;
keyCallbacks = kCFTypeDictionaryKeyCallBacks;
keyCallbacks.equal = NULL;
state.tags = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&keyCallbacks,
(CFDictionaryValueCallBacks *) NULL);
assert(state.tags);
IOCFSerializeBinaryAdd(&state, kOSSerializeBinarySignature, sizeof(kOSSerializeBinarySignature));
ok = DoCFSerializeBinary(&state, object, false);
if (!ok && state.data)
{
CFRelease(state.data);
state.data = NULL; }
if (state.tags) CFRelease(state.tags);
return (state.data);
}
#define setAtIndex(v, idx, o) \
if (idx >= v##Capacity) \
{ \
if (v##Capacity >= v##CapacityMax) ok = false; \
else \
{ \
uint32_t ncap = v##Capacity + 64; \
typeof(v##Array) nbuf = (typeof(v##Array)) malloc(ncap * sizeof(o)); \
if (!nbuf) ok = false; \
else \
{ \
if (v##Array) \
{ \
bcopy(v##Array, nbuf, v##Capacity * sizeof(o)); \
free(v##Array); \
} \
v##Array = nbuf; \
v##Capacity = ncap; \
} \
} \
} \
if (ok) v##Array[idx] = o;
CFTypeRef
IOCFUnserializeBinary(const char * buffer,
size_t bufferSize,
CFAllocatorRef allocator,
CFOptionFlags options __unused,
CFStringRef * errorString)
{
CFTypeRef * objsArray;
uint32_t objsCapacity;
enum { objsCapacityMax = 16*1024*1024 };
uint32_t objsIdx;
CFTypeRef * stackArray;
uint32_t stackCapacity;
enum { stackCapacityMax = 64*1024 };
uint32_t stackIdx;
CFTypeRef result;
CFTypeRef parent;
CFMutableDictionaryRef dict;
CFMutableArrayRef array;
CFMutableSetRef set;
CFMutableDictionaryRef newDict;
CFMutableArrayRef newArray;
CFMutableSetRef newSet;
CFTypeRef o;
CFStringRef sym;
size_t bufferPos;
const uint32_t * next;
uint32_t key, len, wordLen;
bool ok, end, newCollect, isRef;
CFNumberType numType;
CFTypeID type;
const UInt8 * bytes;
if (errorString) *errorString = NULL;
if (0 != strcmp(kOSSerializeBinarySignature, buffer)) return (NULL);
if (3 & ((uintptr_t) buffer)) return (NULL);
if (bufferSize < sizeof(kOSSerializeBinarySignature)) return (NULL);
bufferPos = sizeof(kOSSerializeBinarySignature);
next = (typeof(next)) (((uintptr_t) buffer) + sizeof(kOSSerializeBinarySignature));
DEBG("---------OSUnserializeBinary(%p)\n", buffer);
objsArray = stackArray = NULL;
objsIdx = objsCapacity = 0;
stackIdx = stackCapacity = 0;
result = 0;
parent = 0;
dict = 0;
array = 0;
set = 0;
sym = 0;
ok = true;
while (ok)
{
bufferPos += sizeof(*next);
if (!(ok = (bufferPos <= bufferSize))) break;
key = *next++;
len = (key & kOSSerializeDataMask);
wordLen = (len + 3) >> 2;
end = (0 != (kOSSerializeEndCollecton & key));
DEBG("key 0x%08x: 0x%04x, %d\n", key, len, end);
newCollect = isRef = false;
o = 0; newDict = 0; newArray = 0; newSet = 0;
switch (kOSSerializeTypeMask & key)
{
case kOSSerializeDictionary:
o = newDict = CFDictionaryCreateMutable(allocator, len,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
newCollect = (len != 0);
break;
case kOSSerializeArray:
o = newArray = CFArrayCreateMutable(allocator, len, &kCFTypeArrayCallBacks);
newCollect = (len != 0);
break;
case kOSSerializeSet:
o = newSet = CFSetCreateMutable(allocator, len, &kCFTypeSetCallBacks);
newCollect = (len != 0);
break;
case kOSSerializeObject:
if (len >= objsIdx) break;
o = objsArray[len];
isRef = true;
break;
case kOSSerializeNumber:
bufferPos += sizeof(long long);
if (bufferPos > bufferSize) break;
bytes = (const UInt8 *) &next[0];
if (len <= 32) {
numType = kCFNumberSInt32Type;
} else {
numType = kCFNumberSInt64Type;
}
o = CFNumberCreate(allocator, numType, (const void *) bytes);
next += 2;
break;
case kOSSerializeSymbol:
len--;
case kOSSerializeString:
bufferPos += (wordLen * sizeof(uint32_t));
if (bufferPos > bufferSize) break;
o = CFStringCreateWithBytes(allocator, (const UInt8 *) next, len, kCFStringEncodingUTF8, false);
if (!o)
{
o = CFStringCreateWithBytes(allocator, (const UInt8 *) next, len, kCFStringEncodingMacRoman, false);
syslog(LOG_ERR, "FIXME: IOUnserialize has detected a string that is not valid UTF-8, \"%s\".",
CFStringGetCStringPtr(o, kCFStringEncodingMacRoman));
}
next += wordLen;
break;
case kOSSerializeData:
bufferPos += (wordLen * sizeof(uint32_t));
if (bufferPos > bufferSize) break;
o = CFDataCreate(allocator, (const UInt8 *) next, len);
next += wordLen;
break;
case kOSSerializeBoolean:
o = (len ? kCFBooleanTrue : kCFBooleanFalse);
CFRetain(o);
break;
default:
break;
}
if (!(ok = (o != 0))) break;
if (!isRef)
{
setAtIndex(objs, objsIdx, o);
if (!ok)
{
CFRelease(o);
break;
}
objsIdx++;
}
if (dict)
{
if (sym)
{
if (o != dict) CFDictionarySetValue(dict, sym, o);
sym = 0;
}
else
{
assert(CFStringGetTypeID() == CFGetTypeID(o));
sym = o;
}
}
else if (array) CFArrayAppendValue(array, o);
else if (set) CFSetAddValue(set, o);
else if (result) ok = false;
else
{
assert(!parent);
result = o;
}
if (!ok) break;
if (newCollect)
{
if (!end)
{
stackIdx++;
setAtIndex(stack, stackIdx, parent);
if (!ok) break;
}
DEBG("++stack[%d] %p\n", stackIdx, parent);
parent = o;
dict = newDict;
array = newArray;
set = newSet;
end = false;
}
if (end)
{
if (!stackIdx) break;
parent = stackArray[stackIdx];
DEBG("--stack[%d] %p\n", stackIdx, parent);
stackIdx--;
type = CFGetTypeID(parent);
set = NULL;
dict = NULL;
array = NULL;
if (type == CFDictionaryGetTypeID()) dict = (CFMutableDictionaryRef) parent;
else if (type == CFArrayGetTypeID()) array = (CFMutableArrayRef) parent;
else if (type == CFSetGetTypeID()) set = (CFMutableSetRef) parent;
else ok = false;
}
}
if (!ok) result = 0;
if (objsCapacity)
{
for (len = (result != 0); len < objsIdx; len++) CFRelease(objsArray[len]);
free(objsArray);
}
if (stackCapacity) free(stackArray);
DEBG("ret %p\n", result);
return (result);
}
#endif
CFTypeRef
IOCFUnserializeWithSize(const char * buffer,
size_t bufferSize,
CFAllocatorRef allocator,
CFOptionFlags options,
CFStringRef * errorString)
{
if (errorString) *errorString = NULL;
if (!buffer) return 0;
#if IOKIT_SERVER_VERSION >= 20140421
if (bufferSize < sizeof(kOSSerializeBinarySignature)) return (0);
if ((kIOCFSerializeToBinary & options)
|| (!strcmp(kOSSerializeBinarySignature, buffer))) return (IOCFUnserializeBinary(buffer, bufferSize, allocator, options, errorString));
#else
if (!bufferSize) return (0);
#endif
return (IOCFUnserialize(buffer, allocator, options, errorString));
}