#include <bless.h>
#include <bootfiles.h> // eventually "new" bootcaches.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 <Kernel/IOKit/IOKitKeysPrivate.h>
#include <Kernel/libsa/mkext.h>
#include <DiskArbitration/DiskArbitration.h> // for UUID fetching :P
#include <IOKit/kext/kextmanager_types.h> // DEVMAXPATHSIZE
#include "logging.h"
#include "fat_util.h"
#include "macho_util.h"
#include "mkext_util.h"
#include "bootroot.h" // includes CF
#include "safecalls.h"
MkextCRCResult getMkextCRC(const char * file_path, uint32_t * crc_ptr);
char * copyKernelVersion(const char * kernel_file);
#define NCHARSUUID (2*sizeof(CFUUIDBytes) + 5) // hex with 4 -'s and one NUL
#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->volUUIDStr) CFRelease(caches->volUUIDStr);
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], uuidchars[NCHARSUUID]; \
\
if (!CFStringGetFileSystemRepresentation(relstr, relpath, PATH_MAX)) \
goto finish; \
if(!CFStringGetCString(caches->volUUIDStr, uuidchars, NCHARSUUID, \
kCFStringEncodingASCII)) goto finish; \
if (fillCachedPath(cpath, uuidchars, relpath)) goto finish; \
} while(0)
static struct bootCaches*
parseDict(CFDictionaryRef bcDict, char *rootpath, char **errmsg,
CFStringRef volUUIDStr, CFStringRef volName)
{
struct bootCaches *caches, *rval = NULL;
CFDictionaryRef dict; CFIndex keyCount; CFStringRef str;
*errmsg = "allocation failure";
caches = calloc(1, sizeof(*caches));
if (!caches) goto finish;
caches->cachefd = -1; pathcpy(caches->root, rootpath);
if (!volUUIDStr) goto finish;
caches->volUUIDStr = CFRetain(volUUIDStr);
if (!CFStringGetFileSystemRepresentation(volName,caches->volname,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--; }
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 = "unknown (assumed required) keys in bootcaches.plist";
} else {
*errmsg = NULL;
caches->cacheinfo = CFRetain(bcDict); rval = caches;
}
finish:
if (!rval) {
if (caches) destroyCaches(caches); }
return rval;
}
struct bootCaches* readCaches(char *rootpath)
{
struct bootCaches *rval = NULL;
char *errmsg;
int errnum = 4;
char bcpath[PATH_MAX];
int cfd = -1;
void *bcbuf = NULL;
struct stat bcsb;
CFDictionaryRef bcProps = NULL;
CFDataRef bcData = NULL;
CFDictionaryRef bcDict = NULL;
struct stat sb;
char bsdname[DEVMAXPATHSIZE];
DASessionRef dasession = NULL;
DADiskRef disk = NULL;
CFDictionaryRef ddesc = NULL;
CFUUIDRef voluuid;
CFStringRef volName, uuidStr = NULL;
char bspath[PATH_MAX], uuidchars[NCHARSUUID];
errmsg = "error reading " kBootCachesPath;
if (strlcpy(bcpath, rootpath, PATH_MAX) >= PATH_MAX) goto finish;
if (strlcat(bcpath, kBootCachesPath, PATH_MAX) >= PATH_MAX) goto finish;
if (-1 == (cfd = open(bcpath, O_RDONLY|O_EVTONLY))) {
if (errno == ENOENT) errmsg = NULL;
goto finish;
}
if (fstat(cfd, &bcsb)) goto finish;
if (bcsb.st_uid!= 0) {
if (bcsb.st_uid != 99) { errmsg = kBootCachesPath " not owned by root; no rebuilds";
} else {
errmsg = NULL;
}
goto finish;
}
if (bcsb.st_mode & S_IWGRP || bcsb.st_mode & S_IWOTH) {
errmsg = kBootCachesPath " writable by non-root";
goto finish;
}
if (!(bcbuf = malloc(bcsb.st_size))) goto finish;
if (read(cfd, bcbuf, bcsb.st_size) != bcsb.st_size) goto finish;
if (!(bcData = CFDataCreate(nil, bcbuf, bcsb.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;
errmsg = "couldn't get volume UUID";
if (!(dasession = DASessionCreate(nil))) goto finish;
if (!(devname_r(bcsb.st_dev,S_IFBLK,bsdname,DEVMAXPATHSIZE))) goto finish;
if (!(disk = DADiskCreateFromBSDName(nil, dasession, bsdname))) goto finish;
if (!(ddesc = DADiskCopyDescription(disk))) goto finish;
if (!(voluuid=CFDictionaryGetValue(ddesc,kDADiskDescriptionVolumeUUIDKey)))
goto finish;
if (!(uuidStr = CFUUIDCreateString(nil, voluuid))) goto finish;
if (!(volName=CFDictionaryGetValue(ddesc,kDADiskDescriptionVolumeNameKey)))
goto finish;
errmsg = "bootstamps cache problem";
if (strlcpy(bspath, rootpath, PATH_MAX) >= PATH_MAX) goto finish;
if (strlcat(bspath, kTSCacheDir, PATH_MAX) >= PATH_MAX) goto finish;
if(!CFStringGetCString(uuidStr,uuidchars,NCHARSUUID,kCFStringEncodingASCII))
goto finish;
pathcat(bspath, uuidchars);
if ((errnum = stat(bspath, &sb))) {
if (errno == ENOENT) {
if ((errnum = sdeepmkdir(cfd, bspath, kTSCacheMask))) goto finish;
}
else
goto finish;
}
rval = parseDict(bcDict, rootpath, &errmsg, uuidStr, volName);
if (!rval) goto finish;
rval->cachefd = cfd;
errmsg = NULL;
finish:
if (bcbuf) free(bcbuf);
if (bcData) CFRelease(bcData);
if (bcProps) CFRelease(bcProps);
if (bcDict) CFRelease(bcDict); if (ddesc) CFRelease(ddesc);
if (disk) CFRelease(disk);
if (dasession) CFRelease(dasession);
if (errmsg) {
if (errnum == -1)
kextd_error_log("%s: %s: %s", rootpath, errmsg, strerror(errno));
else
kextd_error_log("%s: %s", rootpath, errmsg);
}
if (!rval) {
if (cfd != -1) close(cfd);
if (uuidStr) CFRelease(uuidStr);
}
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;
} 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_mtimespec.tv_sec; cpath->tstamps[1].tv_usec = rsb.st_mtimespec.tv_nsec / 1000;
if (stat(fulltsp, &tsb) == 0) {
ood = (tsb.st_mtimespec.tv_sec != rsb.st_mtimespec.tv_sec ||
tsb.st_mtimespec.tv_nsec != tsb.st_mtimespec.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 fork_kextcache(char *cacheRoot, char *argv[], Boolean wait)
{
int rval = -2;
int status;
pid_t pid;
char tmpdir[PATH_MAX];
if (strlcpy(tmpdir, cacheRoot, PATH_MAX) >= PATH_MAX) goto finish;
strlcat(tmpdir, kTSCacheDir, PATH_MAX);
switch (pid = fork()) {
case -1:
rval = pid;
goto finish;
case 0: setenv("TMPDIR", tmpdir, 1);
if (!wait) {
if (-1 == (rval = daemon(0, 0))) goto finish;
}
rval = execv("/usr/sbin/kextcache", argv);
kextd_openlog("kextd");
kextd_error_log("couldn't launch kextcache! - %s", strerror(errno));
exit(1);
break;
default: waitpid(pid, &status, 0);
status = WEXITSTATUS(status);
if (wait) {
rval = status;
} else if (status) {
rval = -1;
} else {
rval = pid;
}
break;
}
finish:
if (rval == -1)
kextd_error_log("couldn't fork kextcache!");
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_kextcache(caches->root, kcargs, 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_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("couldn't stat %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;
}
Boolean isBootRoot(char *volroot, Boolean *isGPT)
{
char bsdname[DEVMAXPATHSIZE];
struct stat sb;
CFDictionaryRef binfo = NULL;
Boolean rval = false, gpt = false;
CFArrayRef ar;
if (stat(volroot, &sb)) goto finish;
if (!devname_r(sb.st_dev, S_IFBLK, bsdname, DEVMAXPATHSIZE)) goto finish;
if (BLCreateBooterInformationDictionary(NULL,bsdname,&binfo)) goto finish;
ar = CFDictionaryGetValue(binfo, kBLAuxiliaryPartitionsKey);
rval = (ar && CFArrayGetCount(ar) > 0);
ar = CFDictionaryGetValue(binfo, kBLSystemPartitionsKey);
gpt = (ar && CFArrayGetCount(ar) > 0);
finish:
if (binfo) CFRelease(binfo);
if (isGPT) *isGPT = gpt;
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;
}
Boolean bootedFromDifferentKernel(void)
{
Boolean result = true;
char * runningVersion = NULL; char * onDiskVersion = NULL;
runningVersion = copyKernelVersion(NULL);
onDiskVersion = copyKernelVersion("/mach_kernel");
if (!runningVersion || !onDiskVersion) {
goto finish;
}
if (0 == strcmp(runningVersion, onDiskVersion)) {
result = false;
goto finish;
}
finish:
if (runningVersion) free(runningVersion);
if (onDiskVersion) free(onDiskVersion);
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);
return result;
}
#define KERNEL_VERSION_SYMBOL "_version"
char * copyKernelVersion(const char * kernel_filename)
{
char * result = NULL;
fat_iterator iter = NULL;
if (!kernel_filename) {
size_t vers_length;
int vers_mib_name[] = { CTL_KERN, KERN_VERSION };
if (sysctl(vers_mib_name, sizeof(vers_mib_name) / sizeof(int), NULL,
&vers_length, NULL, 0) != 0) {
kextd_error_log("sysctl for kernel version failed");
goto finish;
}
result = malloc(vers_length * sizeof(char));
if (result == NULL) {
kextd_error_log("malloc failed");
goto finish;
}
if (sysctl(vers_mib_name, sizeof(vers_mib_name) / sizeof(int), result,
&vers_length, NULL, 0) != 0) {
kextd_error_log("sysctl for kernel version failed");
goto finish;
}
} else {
struct mach_header * kernel_file = NULL;
void * kernel_file_end = NULL;
macho_seek_result sym_result;
iter = fat_iterator_open(kernel_filename, 1);
if (!iter) {
goto finish;
}
kernel_file = (struct mach_header *)fat_iterator_find_host_arch(
iter, &kernel_file_end);
if (!kernel_file) {
goto finish;
}
sym_result = macho_find_symbol(
kernel_file, kernel_file_end,
KERNEL_VERSION_SYMBOL, (const void **)&result);
if (sym_result != macho_seek_result_found) {
goto finish;
}
if (result) {
result = strdup(result);
}
}
finish:
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()); }