#include <bless.h>
#include <bootfiles.h>
#include <IOKit/IOKitLib.h>
#include <fcntl.h>
#include <libgen.h>
#include <notify.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <unistd.h>
#include <mach/mach.h>
#include <mach/kmod.h>
#include <Kernel/IOKit/IOKitKeysPrivate.h>
#include <Kernel/libsa/mkext.h>
#include <TargetConditionals.h>
#if !TARGET_OS_EMBEDDED
#include <DiskArbitration/DiskArbitration.h> // for UUID fetching
#include <DiskArbitration/DiskArbitrationPrivate.h> // path -> DADisk
#endif
#include <IOKit/kext/fat_util.h>
#include <IOKit/kext/macho_util.h>
#include <IOKit/kext/kernel_check.h>
#include "bootcaches.h" // includes CF
#include "globals.h"
#include "logging.h"
#include "mkext_util.h"
#include "safecalls.h"
#include "utility.h"
static MkextCRCResult getMkextCRC(const char * file_path, uint32_t * crc_ptr);
#define pathcpy(dst, src) do { \
if (strlcpy(dst, src, PATH_MAX) >= PATH_MAX) goto finish; \
} while(0)
#define pathcat(dst, src) do { \
if (strlcat(dst, src, PATH_MAX) >= PATH_MAX) goto finish; \
} while(0)
void destroyCaches(struct bootCaches *caches)
{
if (caches->cachefd != -1) close(caches->cachefd);
if (caches->cacheinfo) CFRelease(caches->cacheinfo);
if (caches->miscpaths) free(caches->miscpaths); if (caches->rpspaths) free(caches->rpspaths);
free(caches);
}
static void gsub(char old, char new, char *s)
{
char *p;
while((p = s++) && *p)
if (*p == old)
*p = new;
}
int fillCachedPath(cachedPath *cpath, char *uuidchars, char *relpath)
{
int rval = ELAST + 1;
if (strlcat(cpath->tspath, kTSCacheDir, PATH_MAX) >= PATH_MAX) goto finish;
pathcat(cpath->tspath, uuidchars);
pathcat(cpath->tspath, "/");
if (strlcat(cpath->rpath, relpath, PATH_MAX) >= PATH_MAX) goto finish;
gsub('/', ':', relpath);
if (strlcat(cpath->tspath, relpath, PATH_MAX) >= PATH_MAX) goto finish;
rval = 0;
finish:
return rval;
}
#define str2cachedPath(cpath, caches, relstr) \
do { \
char relpath[PATH_MAX]; \
\
if (!CFStringGetFileSystemRepresentation(relstr, relpath, PATH_MAX)) \
goto finish; \
if (fillCachedPath(cpath, caches->uuid_str, relpath)) goto finish; \
} while(0)
static int
finishParse(struct bootCaches *caches, CFDictionaryRef bcDict,
CFDictionaryRef ddesc, char **errmsg)
{
int rval = ELAST + 1;
CFDictionaryRef dict; CFIndex keyCount; CFStringRef str; CFStringRef createdStr = NULL;
CFUUIDRef uuid;
*errmsg = "error getting disk metadata";
if (!(uuid = CFDictionaryGetValue(ddesc, kDADiskDescriptionVolumeUUIDKey)))
goto finish;
if (!(createdStr = CFUUIDCreateString(nil, uuid)))
goto finish;
if (!CFStringGetFileSystemRepresentation(createdStr,caches->uuid_str,NCHARSUUID))
goto finish;
if (!(str = CFDictionaryGetValue(ddesc, kDADiskDescriptionVolumeNameKey)))
goto finish;
if (!CFStringGetFileSystemRepresentation(str, caches->volname, NAME_MAX))
goto finish;
if (!(str = CFDictionaryGetValue(ddesc, kDADiskDescriptionMediaBSDNameKey)))
goto finish;
if (!CFStringGetFileSystemRepresentation(str, caches->bsdname, NAME_MAX))
goto finish;
*errmsg = "unsupported bootcaches data"; keyCount = CFDictionaryGetCount(bcDict);
dict = (CFDictionaryRef)CFDictionaryGetValue(bcDict, kBCPreBootKey);
if (dict) {
CFArrayRef apaths;
CFIndex miscindex = 0;
if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) goto finish;
caches->nmisc = CFDictionaryGetCount(dict); keyCount += CFDictionaryGetCount(dict);
apaths = (CFArrayRef)CFDictionaryGetValue(dict, kBCAdditionalPathsKey);
if (apaths) {
CFIndex acount;
if (CFArrayGetTypeID() != CFGetTypeID(apaths)) goto finish;
acount = CFArrayGetCount(apaths);
caches->nmisc += acount - 1;
if (caches->nmisc > INT_MAX/sizeof(*caches->miscpaths)) goto finish;
caches->miscpaths = (cachedPath*)calloc(caches->nmisc,
sizeof(*caches->miscpaths));
if (!caches->miscpaths) goto finish;
for (; miscindex < acount; miscindex++) {
str = CFArrayGetValueAtIndex(apaths, miscindex);
if (CFGetTypeID(str) != CFStringGetTypeID()) goto finish;
str2cachedPath(&caches->miscpaths[miscindex], caches, str); }
keyCount--; } else {
if (caches->nmisc > INT_MAX/sizeof(*caches->miscpaths)) goto finish;
caches->miscpaths = calloc(caches->nmisc, sizeof(cachedPath));
if (!caches->miscpaths) goto finish;
}
str = (CFStringRef)CFDictionaryGetValue(dict, kBCLabelKey);
if (str) {
if (CFGetTypeID(str) != CFStringGetTypeID()) goto finish;
str2cachedPath(&caches->miscpaths[miscindex], caches, str); caches->label = &caches->miscpaths[miscindex];
miscindex++; keyCount--; }
keyCount--; }
dict = (CFDictionaryRef)CFDictionaryGetValue(bcDict, kBCBootersKey);
if (dict) {
if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) goto finish;
keyCount += CFDictionaryGetCount(dict);
str = (CFStringRef)CFDictionaryGetValue(dict, kBCEFIBooterKey);
if (str) {
if (CFGetTypeID(str) != CFStringGetTypeID()) goto finish;
str2cachedPath(&caches->efibooter, caches, str);
keyCount--; }
str = (CFStringRef)CFDictionaryGetValue(dict, kBCOFBooterKey);
if (str) {
if (CFGetTypeID(str) != CFStringGetTypeID()) goto finish;
str2cachedPath(&caches->ofbooter, caches, str);
keyCount--; }
keyCount--; }
dict = (CFDictionaryRef)CFDictionaryGetValue(bcDict, kBCPostBootKey);
if (dict) {
CFDictionaryRef mkDict;
CFArrayRef apaths;
CFIndex rpsindex = 0;
if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) goto finish;
keyCount += CFDictionaryGetCount(dict);
caches->nrps = CFDictionaryGetCount(dict);
apaths = (CFArrayRef)CFDictionaryGetValue(dict, kBCAdditionalPathsKey);
if (apaths) {
CFIndex acount;
if (CFArrayGetTypeID() != CFGetTypeID(apaths)) goto finish;
acount = CFArrayGetCount(apaths);
caches->nrps += acount - 1;
if (caches->nrps > INT_MAX/sizeof(*caches->rpspaths)) goto finish;
caches->rpspaths = (cachedPath*)calloc(caches->nrps,
sizeof(*caches->rpspaths));
if (!caches->rpspaths) goto finish;
for (; rpsindex < acount; rpsindex++) {
str = CFArrayGetValueAtIndex(apaths, rpsindex);
if (CFGetTypeID(str) != CFStringGetTypeID()) goto finish;
str2cachedPath(&caches->rpspaths[rpsindex], caches, str); }
keyCount--; } else {
if (caches->nrps > INT_MAX/sizeof(*caches->rpspaths)) goto finish;
caches->rpspaths = calloc(caches->nrps, sizeof(cachedPath));
if (!caches->rpspaths) goto finish;
}
str = (CFStringRef)CFDictionaryGetValue(dict, kBCBootConfigKey);
if (str) {
if (CFGetTypeID(str) != CFStringGetTypeID()) goto finish;
str2cachedPath(&caches->rpspaths[rpsindex], caches, str);
caches->bootconfig = &caches->rpspaths[rpsindex++];
keyCount--; }
mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCMKextKey);
if (mkDict) {
if (CFGetTypeID(mkDict) != CFDictionaryGetTypeID()) goto finish;
str = (CFStringRef)CFDictionaryGetValue(mkDict, kBCPathKey);
if (CFGetTypeID(str) != CFStringGetTypeID()) goto finish;
str2cachedPath(&caches->rpspaths[rpsindex], caches, str);
str=(CFStringRef)CFDictionaryGetValue(mkDict, kBCExtensionsDirKey);
if (str) {
char path[PATH_MAX];
if (CFGetTypeID(str) != CFStringGetTypeID()) goto finish;
if (!CFStringGetFileSystemRepresentation(str, path, PATH_MAX))
goto finish;
if (strlcat(caches->exts, path, PATH_MAX) >= PATH_MAX)
goto finish;
}
caches->mkext = &caches->rpspaths[rpsindex++];
keyCount--; }
keyCount--; }
if (keyCount) {
*errmsg = "unrecognized bootcaches data; skipping";
} else {
*errmsg = NULL;
rval = 0;
caches->cacheinfo = CFRetain(bcDict); }
finish:
if (createdStr) CFRelease(createdStr);
return rval;
}
struct bootCaches* readCaches(DADiskRef dadisk)
{
struct bootCaches *rval = NULL, *caches = NULL;
char *errmsg;
int errnum = 4;
struct stat sb;
CFDictionaryRef ddesc = NULL;
CFURLRef volURL = NULL; int ntries = 0;
char bcpath[PATH_MAX];
void *bcbuf = NULL;
CFDataRef bcData = NULL;
CFDictionaryRef bcDict = NULL;
char bspath[PATH_MAX];
errmsg = "allocation failure";
caches = calloc(1, sizeof(*caches));
if (!caches) goto finish;
caches->cachefd = -1;
errmsg = "error copying disk description";
do {
#if TARGET_OS_EMBEDDED
goto finish;
#else
if (!(ddesc = DADiskCopyDescription(dadisk))) goto finish;
#endif
if((volURL=CFDictionaryGetValue(ddesc,kDADiskDescriptionVolumePathKey)))
break;
else
sleep(1);
} while (++ntries < kKXDiskArbMaxRetries);
if (ntries == kKXDiskArbMaxRetries) {
kextd_error_log("Disk description missing mount point for %d tries",
ntries);
} else if (ntries) {
kextd_log("WARNING: readCaches got mount point after %d tries", ntries);
}
if (!CFURLGetFileSystemRepresentation(volURL, false,
(UInt8*)caches->root, PATH_MAX)) goto finish;
errmsg = "error reading " kBootCachesPath;
if (strlcpy(bcpath, caches->root, PATH_MAX) >= PATH_MAX) goto finish;
if (strlcat(bcpath, kBootCachesPath, PATH_MAX) >= PATH_MAX) goto finish;
if (-1 == (caches->cachefd = open(bcpath, O_RDONLY|O_EVTONLY))) {
if (errno == ENOENT) errmsg = NULL;
goto finish;
}
if (fstat(caches->cachefd, &sb))
goto finish;
if (sb.st_uid!= 0) {
errmsg = kBootCachesPath " not owned by root; no rebuilds";
goto finish;
}
if (sb.st_mode & S_IWGRP || sb.st_mode & S_IWOTH) {
errmsg = kBootCachesPath " writable by non-root";
goto finish;
}
if (!(bcbuf = malloc(sb.st_size))) goto finish;
if (read(caches->cachefd, bcbuf, sb.st_size)!=sb.st_size) goto finish;
if (!(bcData = CFDataCreate(nil, bcbuf, sb.st_size))) goto finish;
errmsg = kBootCachesPath " doesn't contain a dictionary";
bcDict = (CFDictionaryRef)CFPropertyListCreateFromXMLData(nil,
bcData, kCFPropertyListImmutable, NULL);
if (!bcDict || CFGetTypeID(bcDict)!=CFDictionaryGetTypeID())
goto finish;
if (finishParse(caches, bcDict, ddesc, &errmsg)) goto finish;
errmsg = "error creating "kTSCacheDir;
if (strlcpy(bspath, caches->root, PATH_MAX) >= PATH_MAX) goto finish;
if (strlcat(bspath, kTSCacheDir, PATH_MAX) >= PATH_MAX) goto finish;
pathcat(bspath, caches->uuid_str);
if ((errnum = stat(bspath, &sb))) {
if (errno == ENOENT) {
if ((errnum = sdeepmkdir(caches->cachefd, bspath, kTSCacheMask)))
goto finish;
}
else
goto finish;
}
rval = caches;
errmsg = NULL;
finish:
if (errmsg) {
if (errnum == -1)
kextd_error_log("%s: %s: %s",caches->root,errmsg,strerror(errno));
else
kextd_error_log("%s: %s", caches->root, errmsg);
}
if (ddesc) CFRelease(ddesc);
if (bcDict) CFRelease(bcDict); if (bcData) CFRelease(bcData);
if (bcbuf) free(bcbuf);
if (!rval)
destroyCaches(caches);
return rval;
}
int needsUpdate(char *root, cachedPath* cpath, Boolean *outofdate)
{
Boolean ood;
int bsderr = -1;
struct stat rsb, tsb;
char fullrp[PATH_MAX], fulltsp[PATH_MAX];
pathcpy(fullrp, root);
pathcat(fullrp, cpath->rpath);
pathcpy(fulltsp, root);
pathcat(fulltsp, cpath->tspath);
if (stat(fullrp, &rsb)) {
if (errno == ENOENT) {
bsderr = 0;
*outofdate = false;
} else {
kextd_error_log("cached file %s: %s", fullrp, strerror(errno));
}
goto finish;
}
cpath->tstamps[0].tv_sec = rsb.st_atimespec.tv_sec; cpath->tstamps[0].tv_usec = rsb.st_atimespec.tv_nsec / 1000;
cpath->tstamps[1].tv_sec = rsb.st_ctimespec.tv_sec; cpath->tstamps[1].tv_usec = rsb.st_ctimespec.tv_nsec / 1000;
if (stat(fulltsp, &tsb) == 0) {
ood = (tsb.st_mtimespec.tv_sec != rsb.st_ctimespec.tv_sec ||
tsb.st_mtimespec.tv_nsec != rsb.st_ctimespec.tv_nsec);
} else {
if (errno == ENOENT) {
ood = true; } else {
kextd_error_log("cached file %s: %s", fulltsp, strerror(errno));
goto finish;
}
}
*outofdate = ood;
bsderr = 0;
finish:
return bsderr;
}
int needUpdates(struct bootCaches *caches, Boolean *any,
Boolean *rps, Boolean *booters, Boolean *misc)
{
int rval = 0; Boolean needsUp, rpsOOD, bootersOOD, miscOOD, anyOOD;
cachedPath *cp;
rpsOOD = bootersOOD = miscOOD = anyOOD = false;
for (cp = caches->rpspaths; cp < &caches->rpspaths[caches->nrps]; cp++) {
if ((rval = needsUpdate(caches->root, cp, &needsUp))) goto finish;
if (needsUp) anyOOD = rpsOOD = true;
}
if ((cp = &(caches->efibooter)), cp->rpath[0]) {
if ((rval = needsUpdate(caches->root, cp, &needsUp))) goto finish;
if (needsUp) anyOOD = bootersOOD = true;
}
if ((cp = &(caches->ofbooter)), cp->rpath[0]) {
if ((rval = needsUpdate(caches->root, cp, &needsUp))) goto finish;
if (needsUp) anyOOD = bootersOOD = true;
}
for (cp = caches->miscpaths; cp < &caches->miscpaths[caches->nmisc]; cp++){
(void)needsUpdate(caches->root, cp, &needsUp);
if (needsUp) anyOOD = miscOOD = true;
}
if (rps) *rps = rpsOOD;
if (booters) *booters = bootersOOD;
if (misc) *misc = miscOOD;
if (any) *any = anyOOD;
finish:
return rval;
}
static int applyStamp(char *root, cachedPath *cpath, int fdvol)
{
int bsderr = -1, fd;
char tspath[PATH_MAX];
pathcpy(tspath, root);
pathcat(tspath, cpath->tspath);
(void)sunlink(fdvol, tspath); if (-1 == (fd = sopen(fdvol, tspath, O_WRONLY|O_CREAT, kTSCacheMask)))
goto finish;
bsderr = futimes(fd, cpath->tstamps);
finish:
return bsderr;
}
int applyStamps(struct bootCaches *caches)
{
int rval = 0;
cachedPath *cp;
for (cp = caches->rpspaths; cp < &caches->rpspaths[caches->nrps]; cp++) {
rval |= applyStamp(caches->root, cp, caches->cachefd);
}
if ((cp = &(caches->efibooter)), cp->rpath[0]) {
rval |= applyStamp(caches->root, cp, caches->cachefd);
}
if ((cp = &(caches->ofbooter)), cp->rpath[0]) {
rval |= applyStamp(caches->root, cp, caches->cachefd);
}
for (cp = caches->miscpaths; cp < &caches->miscpaths[caches->nmisc]; cp++){
rval |= applyStamp(caches->root, cp, caches->cachefd);
}
return rval;
}
int rebuild_mkext(struct bootCaches *caches, Boolean wait)
{
int rval = ELAST + 1;
int pid = -1;
CFIndex i, argi = 0, argc = 0, narchs = 0;
CFDictionaryRef pbDict, mkDict;
CFArrayRef archArray;
char **kcargs = NULL, **archstrs = NULL; char fullmkextp[PATH_MAX], fullextsp[PATH_MAX];
pbDict = CFDictionaryGetValue(caches->cacheinfo, kBCPostBootKey);
if (!pbDict || CFGetTypeID(pbDict) != CFDictionaryGetTypeID()) goto finish;
mkDict = CFDictionaryGetValue(pbDict, kBCMKextKey);
if (!mkDict || CFGetTypeID(mkDict) != CFDictionaryGetTypeID()) goto finish;
archArray = CFDictionaryGetValue(mkDict, kBCArchsKey);
if (archArray) {
narchs = CFArrayGetCount(archArray);
archstrs = calloc(narchs, sizeof(char*));
if (!archstrs) goto finish;
}
argc = 1 + narchs*2 + 1 + 1 + 1 + 1 + 1;
kcargs = malloc(argc * sizeof(char*));
if (!kcargs) goto finish;
kcargs[argi++] = "kextcache";
for(i = 0; i < narchs; i++) {
CFStringRef archStr;
size_t archSize;
archStr = CFArrayGetValueAtIndex(archArray, i);
if (!archStr || CFGetTypeID(archStr)!=CFStringGetTypeID()) goto finish;
archSize = CFStringGetMaximumSizeOfFileSystemRepresentation(archStr);
if (!archSize) goto finish;
archstrs[i] = malloc(archSize);
if (!archstrs[i]) goto finish;
if (!CFStringGetFileSystemRepresentation(archStr,archstrs[i],archSize))
goto finish;
kcargs[argi++] = "-a";
kcargs[argi++] = archstrs[i];
}
kcargs[argi++] = "-l";
kcargs[argi++] = "-m";
pathcpy(fullmkextp, caches->root);
pathcat(fullmkextp, caches->mkext->rpath);
kcargs[argi++] = fullmkextp;
pathcpy(fullextsp, caches->root);
pathcat(fullextsp, caches->exts);
kcargs[argi++] = fullextsp;
kcargs[argi] = NULL;
rval = 0;
pid = fork_program("/usr/sbin/kextcache", kcargs, 0 , wait);
finish:
if (rval) kextd_error_log("data error before mkext rebuild");
if (wait || pid < 0)
rval = pid;
if (archstrs) {
for (i = 0; i < narchs; i++) {
if (archstrs[i]) free(archstrs[i]);
}
free(archstrs);
}
if (kcargs) free(kcargs);
return rval;
}
Boolean check_plist_cache(struct bootCaches *caches)
{
Boolean needsrebuild = false;
struct stat sb;
struct stat extsb;
char fullplistcachep[PATH_MAX], fullextsp[PATH_MAX];
pathcpy(fullplistcachep, caches->root);
pathcat(fullplistcachep, caches->exts);
pathcat(fullplistcachep, "/Caches/com.apple.kext.info");
pathcpy(fullextsp, caches->root);
pathcat(fullextsp, caches->exts);
if (stat(fullextsp, &extsb) == -1) {
kextd_log("WARNING: %s: %s", caches->exts, strerror(errno));
goto finish;
}
needsrebuild = true; if (stat(fullplistcachep, &sb) == -1)
goto finish;
needsrebuild = (sb.st_mtime != extsb.st_mtime + 1);
finish:
return needsrebuild;
}
Boolean check_mkext(struct bootCaches *caches)
{
Boolean needsrebuild = false;
struct stat sb;
char fullmkextp[PATH_MAX], fullextsp[PATH_MAX];
pathcpy(fullmkextp, caches->root);
pathcat(fullmkextp, caches->mkext->rpath);
pathcpy(fullextsp, caches->root);
pathcat(fullextsp, caches->exts);
if (caches->mkext) {
struct stat extsb;
if (stat(fullextsp, &extsb) == -1) {
kextd_log("WARNING: %s: %s", caches->exts, strerror(errno));
goto finish;
}
needsrebuild = true; if (stat(fullmkextp, &sb) == -1)
goto finish;
needsrebuild = (sb.st_mtime != extsb.st_mtime + 1);
}
finish:
return needsrebuild;
}
DADiskRef createDiskForMount(DASessionRef session, const char *mount)
{
DADiskRef rval = NULL;
DASessionRef dasession = NULL;
CFURLRef volURL = NULL;
if (session) {
dasession = session;
} else {
#if !TARGET_OS_EMBEDDED
dasession = DASessionCreate(nil);
#endif
if (!dasession) goto finish;
}
volURL = CFURLCreateFromFileSystemRepresentation(nil, (UInt8*)mount,
strlen(mount), 1 );
if (!volURL) goto finish;
#if !TARGET_OS_EMBEDDED
rval = DADiskCreateFromVolumePath(nil, dasession, volURL);
#endif
finish:
if (volURL) CFRelease(volURL);
if (!session && dasession)
CFRelease(dasession);
return rval;
}
Boolean hasBoots(char *bsdname, CFArrayRef *auxPartsCopy, Boolean *isGPT)
{
CFDictionaryRef binfo = NULL;
Boolean rval = false, gpt = false;
int err;
CFArrayRef ar;
char * errmsg = NULL;
char stack_bsdname[DEVMAXPATHSIZE];
char * lookup_bsdname = bsdname;
CFArrayRef dataPartitions = NULL; char fulldevname[MAXPATHLEN];
char parentdevname[MAXPATHLEN];
uint32_t partitionNum;
BLPartitionType partitionType;
if (BLCreateBooterInformationDictionary(NULL,bsdname,&binfo)) goto finish;
dataPartitions = CFDictionaryGetValue(binfo, kBLDataPartitionsKey);
if (dataPartitions && CFArrayGetCount(dataPartitions)) {
CFStringRef dpBsdName = CFArrayGetValueAtIndex(dataPartitions, 0);
if (dpBsdName) {
errmsg = "string conversion failure for bsdname";
if (!CFStringGetCString(dpBsdName, stack_bsdname,
sizeof(stack_bsdname), kCFStringEncodingUTF8)) {
goto finish;
}
lookup_bsdname = stack_bsdname;
}
}
errmsg = "internal error";
if (sizeof(fulldevname) <= snprintf(fulldevname, sizeof(fulldevname),
"/dev/%s", lookup_bsdname)) {
goto finish;
}
errmsg = "can't get partition type";
if (err = BLGetParentDeviceAndPartitionType(NULL ,
fulldevname, parentdevname, &partitionNum, &partitionType)) {
goto finish;
}
if (partitionType == kBLPartitionType_GPT) {
gpt = true;
}
errmsg = "can't get helper partitions";
ar = CFDictionaryGetValue(binfo, kBLAuxiliaryPartitionsKey);
rval = (ar && CFArrayGetCount(ar) > 0);
if (auxPartsCopy)
*auxPartsCopy = CFRetain(ar);
errmsg = NULL;
finish:
if (binfo) CFRelease(binfo);
if (isGPT) *isGPT = gpt;
if (errmsg) {
kextd_error_log(errmsg);
}
return rval;
}
Boolean bootedFromDifferentMkext(void)
{
Boolean result = true;
MkextCRCResult startupCrcFound;
MkextCRCResult onDiskCrcFound;
uint32_t startupCrc;
uint32_t onDiskCrc;
startupCrcFound = getMkextCRC(NULL, &startupCrc);
if (startupCrcFound != kMkextCRCFound) {
result = false;
goto finish;
}
onDiskCrcFound = getMkextCRC("/System/Library/Extensions.mkext",
&onDiskCrc);
if (onDiskCrcFound != kMkextCRCFound) {
goto finish;
}
if (startupCrc == onDiskCrc) {
result = false;
}
finish:
return result;
}
MkextCRCResult getMkextCRC(const char * file_path, uint32_t * crc_ptr)
{
MkextCRCResult result = kMkextCRCError;
fat_iterator iter = NULL;
const void * file_start = NULL;
void * file_end = NULL;
mkext_header * mkext_hdr;
io_registry_entry_t ioRegRoot = MACH_PORT_NULL;
CFTypeRef regObj = NULL; CFDataRef dataObj = NULL; CFIndex numBytes;
uint32_t crc;
if (!file_path) {
ioRegRoot = IORegistryGetRootEntry(kIOMasterPortDefault);
if (ioRegRoot != MACH_PORT_NULL) {
regObj = IORegistryEntryCreateCFProperty(ioRegRoot,
CFSTR(kIOStartupMkextCRC), kCFAllocatorDefault, kNilOptions);
if (!regObj) {
result = kMkextCRCNotFound;
goto finish;
}
if (CFGetTypeID(regObj) != CFDataGetTypeID()) {
goto finish;
}
}
dataObj = (CFDataRef)regObj;
numBytes = CFDataGetLength(dataObj);
if (numBytes != sizeof(uint32_t)) {
goto finish;
}
CFDataGetBytes(dataObj, CFRangeMake(0, numBytes), (void *)&crc);
} else {
iter = fat_iterator_open(file_path, 0);
if (!iter) {
goto finish;
}
file_start = fat_iterator_file_start(iter);
if (!file_start) {
goto finish;
}
if (ISMKEXT(MAGIC32(file_start))) {
mkext_hdr = (struct mkext_header *)file_start;
} else {
file_start = fat_iterator_find_host_arch(
iter, &file_end);
if (!file_start) {
goto finish;
}
if (!ISMKEXT(MAGIC32(file_start))) {
goto finish;
}
mkext_hdr = (struct mkext_header *)file_start;
}
crc = OSSwapBigToHostInt32(mkext_hdr->adler32);
}
*crc_ptr = crc;
result = kMkextCRCFound;
finish:
if (ioRegRoot) IOObjectRelease(ioRegRoot);
if (dataObj) CFRelease(dataObj);
if (iter) fat_iterator_close(iter);
return result;
}
void _daDone(DADiskRef disk, DADissenterRef dissenter, void *ctx)
{
if (dissenter)
CFRetain(dissenter);
*(DADissenterRef*)ctx = dissenter;
CFRunLoopStop(CFRunLoopGetCurrent()); }