#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 <System/libkern/mkext.h>
#include <System/libkern/OSKextLibPrivate.h>
#include <DiskArbitration/DiskArbitration.h> // for UUID fetching
#include <DiskArbitration/DiskArbitrationPrivate.h> // path -> DADisk
#include <IOKit/kext/fat_util.h>
#include <IOKit/kext/macho_util.h>
#include "fork_program.h"
#include "bootcaches.h" // includes CF
#include <IOKit/kext/OSKext.h>
#include <IOKit/kext/OSKextPrivate.h>
#include "kextd_globals.h"
#include "safecalls.h"
#include "kext_tools_util.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 = "invalid bootcaches.plist 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--; }
if (CFDictionaryGetValue(dict, kBCMKextKey) &&
CFDictionaryGetValue(dict, kBCMKext2Key)) {
*errmsg = "multiple mkext keys found";
goto finish;
}
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--; }
mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCMKext2Key);
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.plist 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; strlcpy(caches->root, "<unknown>", PATH_MAX);
errmsg = "error copying disk description";
do {
if (!(ddesc = DADiskCopyDescription(dadisk))) goto finish;
if((volURL=CFDictionaryGetValue(ddesc,kDADiskDescriptionVolumePathKey)))
break;
else
sleep(1);
} while (++ntries < kKextdDiskArbMaxRetries);
if (ntries == kKextdDiskArbMaxRetries) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag | kOSKextLogFileAccessFlag,
"Disk description missing mount point for %d tries",
ntries);
} else if (ntries) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogGeneralFlag | kOSKextLogFileAccessFlag,
"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;
}
caches->sb = sb; 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) {
struct statfs sfs;
if (statfs(caches->root, &sfs) == 0 &&
(sfs.f_flags & MNT_IGNORE_OWNERSHIP) == 0) {
if ((errnum = sdeepmkdir(caches->cachefd, bspath, kTSCacheMask))) {
goto finish;
}
}
} else {
goto finish;
}
}
rval = caches;
errmsg = NULL;
finish:
if (errmsg) {
if (errnum == -1) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s: %s: %s.", caches->root, errmsg, strerror(errno));
} else {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%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 {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"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 {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"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;
int mkextVersion = 2; CFArrayRef archArray;
char **kcargs = NULL, **archstrs = NULL; char * lastslash = NULL;
char tpath[PATH_MAX];
struct stat sb;
char fullmkextp[PATH_MAX], fullmkextdirp[PATH_MAX], fullextsp[PATH_MAX];
if (!caches->mkext)
goto finish;
pbDict = CFDictionaryGetValue(caches->cacheinfo, kBCPostBootKey);
if (!pbDict || CFGetTypeID(pbDict) != CFDictionaryGetTypeID()) goto finish;
mkDict = CFDictionaryGetValue(pbDict, kBCMKext2Key);
if (!mkDict) {
mkDict = CFDictionaryGetValue(pbDict, kBCMKextKey);
mkextVersion = 1;
}
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 + 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";
pathcpy(tpath, caches->root);
pathcat(tpath, "/etc/rc.cdrom");
if (stat(tpath, &sb) == 0)
kcargs[argi++] = "-n";
if (mkextVersion == 2) {
kcargs[argi++] = "-mkext2";
} else if (mkextVersion == 1) {
kcargs[argi++] = "-mkext1";
} else {
goto finish;
}
pathcpy(fullmkextp, caches->root);
pathcat(fullmkextp, caches->mkext->rpath);
kcargs[argi++] = fullmkextp;
kcargs[argi++] = "-volume-root";
kcargs[argi++] = caches->root;
pathcpy(fullextsp, caches->root);
pathcat(fullextsp, caches->exts);
kcargs[argi++] = fullextsp;
kcargs[argi] = NULL;
pathcpy(fullmkextdirp, fullmkextp);
lastslash = rindex(fullmkextdirp, '/');
if (lastslash) {
*lastslash = '\0';
if ((rval = sdeepmkdir(caches->cachefd, fullmkextdirp, kSysCacheMask))) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"failed to create mkext folder %s.", fullmkextdirp);
goto finish;
}
}
rval = 0;
pid = fork_program("/usr/sbin/kextcache", kcargs, wait);
finish:
if (rval) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"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 directoryPlistCachesNeedRebuild(
CFURLRef directoryURL,
const NXArchInfo * kernelArchInfo)
{
Boolean result = true;
CFStringRef cacheBasename = NULL;
if (!_OSKextReadCache(directoryURL, CFSTR(_kOSKextIdentifierCacheBasename),
NULL, _kOSKextCacheFormatCFBinary, false,
NULL)) {
goto finish;
}
if (!_OSKextReadCache(directoryURL, CFSTR(kIOKitPersonalitiesKey),
kernelArchInfo, _kOSKextCacheFormatIOXML, false,
NULL)) {
goto finish;
}
cacheBasename = CFStringCreateWithFormat(kCFAllocatorDefault,
NULL, CFSTR("%s%s"),
_kKextPropertyValuesCacheBasename,
"OSBundleHelper");
if (!cacheBasename) {
OSKextLogMemError();
result = false; goto finish;
}
if (!_OSKextReadCache(directoryURL, cacheBasename,
kernelArchInfo, _kOSKextCacheFormatCFXML, false,
NULL)) {
goto finish;
}
result = false;
finish:
SAFE_RELEASE(cacheBasename);
return result;
}
Boolean plistCachesNeedRebuild(const NXArchInfo * kernelArchInfo)
{
Boolean result = true;
CFArrayRef systemExtensionsFolderURLs = NULL; CFIndex count, i;
systemExtensionsFolderURLs = OSKextGetSystemExtensionsFolderURLs();
if (!systemExtensionsFolderURLs ||
!CFArrayGetCount(systemExtensionsFolderURLs)) {
result = false;
goto finish;
}
count = CFArrayGetCount(systemExtensionsFolderURLs);
for (i = 0; i < count; i++) {
CFURLRef directoryURL = CFArrayGetValueAtIndex(
systemExtensionsFolderURLs, i);
if (directoryPlistCachesNeedRebuild(directoryURL, kernelArchInfo)) {
goto finish;
}
}
result = false;
finish:
return result;
}
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) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogFileAccessFlag,
"Warning: %s: %s", fullextsp, 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 {
dasession = DASessionCreate(nil);
if (!dasession) goto finish;
}
volURL = CFURLCreateFromFileSystemRepresentation(nil, (UInt8*)mount,
strlen(mount), 1 );
if (!volURL) goto finish;
rval = DADiskCreateFromVolumePath(nil, dasession, volURL);
finish:
if (volURL) CFRelease(volURL);
if (!session && dasession)
CFRelease(dasession);
return rval;
}
Boolean hasBootRootBoots(struct bootCaches *caches, CFArrayRef *auxPartsCopy, Boolean *isAPM)
{
CFDictionaryRef binfo = NULL;
Boolean rval = false, apm = false;
int err;
CFArrayRef ar;
char * errmsg = NULL;
char stack_bsdname[DEVMAXPATHSIZE];
char * lookup_bsdname = caches->bsdname;
CFArrayRef dataPartitions = NULL; char fulldevname[PATH_MAX];
char parentdevname[PATH_MAX];
uint32_t partitionNum;
BLPartitionType partitionType;
if (BLCreateBooterInformationDictionary(NULL, lookup_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_APM) {
apm = true;
}
if (apm) {
CFDictionaryRef pbDict, mk2Dict;
errmsg = NULL;
pbDict = CFDictionaryGetValue(caches->cacheinfo, kBCPostBootKey);
if (!pbDict || CFGetTypeID(pbDict) != CFDictionaryGetTypeID()) goto finish;
mk2Dict = CFDictionaryGetValue(pbDict, kBCMKext2Key);
if (!mk2Dict && caches->ofbooter.rpath[0] == '\0')
goto finish;
}
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 (isAPM) *isAPM = apm;
if (errmsg) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s: %s", caches->root, 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(_kOSKextStartupMkextPath, &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;
const void * arch_start = NULL;
void * arch_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(kOSStartupMkextCRC), 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 (*(uint32_t *)(file_start) == OSSwapHostToBigInt32(MKEXT_MAGIC)) {
goto finish;
} else {
const NXArchInfo * runningKernelArch =
OSKextGetRunningKernelArchitecture();
if (!runningKernelArch) {
goto finish;
}
arch_start = fat_iterator_find_arch(
iter,
runningKernelArch->cputype, runningKernelArch->cpusubtype,
&arch_end);
if (!arch_start) {
goto finish;
}
if (*(uint32_t *)(arch_start) != OSSwapHostToBigInt32(MKEXT_MAGIC)) {
goto finish;
}
mkext_hdr = (struct mkext_header *)arch_start;
}
crc = MKEXT_GET_CHECKSUM(mkext_hdr);
}
*crc_ptr = crc;
result = kMkextCRCFound;
finish:
if (ioRegRoot) IOObjectRelease(ioRegRoot);
if (regObj) CFRelease(regObj);
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()); }