#include <bless.h>
#include <bootfiles.h>
#include <IOKit/IOKitLib.h>
#include <fcntl.h>
#include <libgen.h>
#include <notify.h>
#include <paths.h>
#include <mach/mach.h>
#include <mach/kmod.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <EFILogin/EFILogin.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 <IOKit/storage/CoreStorage/CoreStorageUserLib.h>
#include <IOKit/storage/CoreStorage/CoreStorageCryptoIDs.h>
#include <IOKit/storage/CoreStorage/CSFullDiskEncryption.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 void removeTrailingSlashes(char * path);
#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);
if (caches->csfde_uuid) CFRelease(caches->csfde_uuid);
free(caches);
}
static void gsub(char old, char new, char *s)
{
char *p;
while((p = s++) && *p)
if (*p == old)
*p = new;
}
static int
MAKE_CACHEDPATH(cachedPath *cpath, struct bootCaches *caches,
CFStringRef relstr)
{
int rval;
size_t fullLen;
char tsname[NAME_MAX];
rval = EINVAL;
if (!(relstr)) goto finish;
if (CFGetTypeID(relstr) != CFStringGetTypeID()) goto finish;
rval = EOVERFLOW;
if (!CFStringGetFileSystemRepresentation(relstr, cpath->rpath,
sizeof(cpath->rpath))) {
goto finish;
}
if (strlcpy(tsname, cpath->rpath, sizeof(tsname)) >= sizeof(tsname))
goto finish;
gsub('/', ':', tsname);
fullLen = snprintf(cpath->tspath, sizeof(cpath->tspath), "%s/%s/%s",
kTSCacheDir, caches->fsys_uuid, tsname);
if (fullLen >= sizeof(cpath->tspath))
goto finish;
rval = 0;
finish:
return rval;
}
static int
extractProps(struct bootCaches *caches, CFDictionaryRef bcDict,
CFDictionaryRef ddesc, char **errmsg)
{
int rval = ELAST + 1;
CFDictionaryRef dict; CFIndex keyCount; CFIndex rpsindex = 0; 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->fsys_uuid,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 = "unrecognized 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 = (int)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 ((unsigned int)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);
MAKE_CACHEDPATH(&caches->miscpaths[miscindex], caches, str);
}
keyCount--; } else {
if ((unsigned int)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) {
MAKE_CACHEDPATH(&caches->miscpaths[miscindex], caches, str);
caches->label = &caches->miscpaths[miscindex];
miscindex++; #pragma unused(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) {
MAKE_CACHEDPATH(&caches->efibooter, caches, str);
keyCount--; }
str = (CFStringRef)CFDictionaryGetValue(dict, kBCOFBooterKey);
if (str) {
MAKE_CACHEDPATH(&caches->ofbooter, caches, str);
keyCount--; }
keyCount--; }
dict = (CFDictionaryRef)CFDictionaryGetValue(bcDict, kBCPostBootKey);
if (dict) {
CFDictionaryRef mkDict, erDict;
CFArrayRef apaths;
CFIndex acount;
Boolean isKernelcache = false;
int kcacheKeys = 0;
if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) goto finish;
keyCount += CFDictionaryGetCount(dict);
caches->nrps = (int)CFDictionaryGetCount(dict);
apaths = (CFArrayRef)CFDictionaryGetValue(dict, kBCAdditionalPathsKey);
if (apaths) {
if (CFArrayGetTypeID() != CFGetTypeID(apaths)) goto finish;
acount = CFArrayGetCount(apaths);
caches->nrps += (acount - 1);
}
erDict=(CFDictionaryRef)CFDictionaryGetValue(dict,kBCEncryptedRootKey);
if (erDict) {
if (CFGetTypeID(erDict)!=CFDictionaryGetTypeID()) goto finish;
caches->nrps++;
if (CFDictionaryGetValue(erDict,kBCCSFDELocalizedResourcesCache)) {
caches->nrps++;
}
}
if ((unsigned int)caches->nrps > INT_MAX/sizeof(*caches->rpspaths))
goto finish;
caches->rpspaths = (cachedPath*)calloc(caches->nrps,
sizeof(*caches->rpspaths));
if (!caches->rpspaths) goto finish;
if (apaths) {
for (; rpsindex < acount; rpsindex++) {
str = CFArrayGetValueAtIndex(apaths, rpsindex);
MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str);
}
keyCount--; }
str = (CFStringRef)CFDictionaryGetValue(dict, kBCBootConfigKey);
if (str) {
MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str);
caches->bootconfig = &caches->rpspaths[rpsindex++];
keyCount--; }
if (erDict) {
keyCount += CFDictionaryGetCount(erDict);
str = CFDictionaryGetValue(erDict, kBCCSFDEPropertyCache);
if (str) {
MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str);
caches->erpropcache = &caches->rpspaths[rpsindex++];
keyCount--;
}
str = CFDictionaryGetValue(erDict, kBCCSFDEDefaultResourcesDir);
if (!str) str=CFDictionaryGetValue(erDict,CFSTR("ResourcesDir"));
if (str) {
MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str);
caches->efidefrsrcs = &caches->rpspaths[rpsindex++];
keyCount--;
}
str = CFDictionaryGetValue(erDict,kBCCSFDELocalizedResourcesCache);
if (str) {
MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str);
caches->efiloccache = &caches->rpspaths[rpsindex++];
keyCount--;
str = CFDictionaryGetValue(erDict, kBCCSFDELocalizationSource);
if (str && CFGetTypeID(str) == CFStringGetTypeID() &&
CFStringGetFileSystemRepresentation(str,
caches->locSource, sizeof(caches->locSource))) {
keyCount--;
} else {
goto finish;
}
str = CFDictionaryGetValue(erDict, kBCCSFDELanguagesPref);
if (str && CFGetTypeID(str) == CFStringGetTypeID() &&
CFStringGetFileSystemRepresentation(str, caches->locPref,
sizeof(caches->locPref))) {
keyCount--;
} else {
goto finish;
}
}
keyCount--; }
kcacheKeys = 0;
if (CFDictionaryContainsKey(dict, kBCMKextKey)) kcacheKeys++;
if (CFDictionaryContainsKey(dict, kBCMKext2Key)) kcacheKeys++;
if (CFDictionaryContainsKey(dict, kBCKernelcacheV1Key)) kcacheKeys++;
if (kcacheKeys > 1) {
*errmsg = "multiple cache keys found";
goto finish;
}
do {
mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCKernelcacheV1Key);
if (mkDict) {
isKernelcache = true;
break;
}
mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCMKext2Key);
if (mkDict) break;
mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCMKextKey);
if (mkDict) break;
} while (0);
if (mkDict) {
if (CFGetTypeID(mkDict) != CFDictionaryGetTypeID()) {
goto finish;
}
str = (CFStringRef)CFDictionaryGetValue(mkDict, kBCPathKey);
MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str); caches->kext_boot_cache_file = &caches->rpspaths[rpsindex++];
#pragma unused(rpsindex)
str = (CFStringRef)CFDictionaryGetValue(mkDict, kBCExtensionsDirKey);
if (!str || CFGetTypeID(str) != CFStringGetTypeID()) {
goto finish;
}
if (!CFStringGetFileSystemRepresentation(str, caches->exts,
sizeof(caches->exts))) {
goto finish;
}
if (isKernelcache) {
str = (CFStringRef)CFDictionaryGetValue(mkDict, kBCKernelPathKey);
if (!str || CFGetTypeID(str) != CFStringGetTypeID()) goto finish;
if (!CFStringGetFileSystemRepresentation(str, caches->kernel,
sizeof(caches->kernel))) {
goto finish;
}
}
keyCount--; }
keyCount--; }
if (keyCount || (unsigned)rpsindex != caches->nrps) {
*errmsg = "unrecognized bootcaches.plist data; skipping";
errno = EINVAL;
} else {
*errmsg = NULL;
rval = 0;
caches->cacheinfo = CFRetain(bcDict); }
finish:
if (createdStr) CFRelease(createdStr);
return rval;
}
static int
createCacheDirs(struct bootCaches *caches)
{
int errnum;
char *errmsg;
struct stat sb;
char cachedir[PATH_MAX], uuiddir[PATH_MAX];
errmsg = "error creating " kTSCacheDir;
pathcpy(cachedir, caches->root);
pathcat(cachedir, kTSCacheDir);
pathcpy(uuiddir, cachedir);
pathcat(uuiddir, "/");
pathcat(uuiddir, caches->fsys_uuid);
if ((errnum = stat(uuiddir, &sb))) {
if (errno == ENOENT) {
if (stat(cachedir, &sb) == 0) {
(void)sdeepunlink(caches->cachefd, cachedir);
}
if ((errnum = sdeepmkdir(caches->cachefd,uuiddir,kCacheDirMode))){
goto finish;
}
} else {
goto finish;
}
}
if (caches->erpropcache) {
errmsg = "error creating encrypted root property cache dir";
pathcpy(cachedir, caches->root);
pathcat(cachedir, dirname(caches->erpropcache->rpath));
if ((errnum = stat(cachedir, &sb))) {
if (errno == ENOENT) {
errnum=sdeepmkdir(caches->cachefd,cachedir,kCacheDirMode);
if (errnum) goto finish;
} else {
goto finish;
}
}
}
if (caches->efiloccache) {
errmsg = "error creating localized resources cache dir";
pathcpy(cachedir, caches->root);
pathcat(cachedir, caches->efiloccache->rpath);
if ((errnum = stat(cachedir, &sb))) {
if (errno == ENOENT) {
errnum=sdeepmkdir(caches->cachefd,cachedir,kCacheDirMode);
if (errnum) goto finish;
} else {
goto finish;
}
}
}
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);
}
errno = 0;
}
return errnum;
}
static CFDictionaryRef
copy_dict_from_fd(int fd, struct stat *sb)
{
CFDictionaryRef rval = NULL;
void *buf = NULL;
CFDataRef data = NULL;
CFDictionaryRef dict = NULL;
if (sb->st_size > UINT_MAX || sb->st_size > LONG_MAX) goto finish;
if (!(buf = malloc((size_t)sb->st_size))) goto finish;
if (read(fd, buf, (size_t)sb->st_size) != sb->st_size)
goto finish;
if (!(data = CFDataCreate(nil, buf, (long)sb->st_size)))
goto finish;
dict = (CFDictionaryRef)CFPropertyListCreateFromXMLData(nil,
data, kCFPropertyListImmutable, NULL);
if (!dict || CFGetTypeID(dict)!=CFDictionaryGetTypeID()) {
goto finish;
}
rval = CFRetain(dict);
finish:
if (dict) CFRelease(dict); if (data) CFRelease(data);
if (buf) free(buf);
return rval;
}
static struct bootCaches*
readCaches_internal(CFURLRef volURL, DADiskRef dadisk)
{
struct bootCaches *rval = NULL, *caches = NULL;
char *errmsg;
int errnum = 4;
struct stat sb;
CFDictionaryRef ddesc = NULL;
char bcpath[PATH_MAX];
CFDictionaryRef bcDict = NULL;
struct statfs rootsfs;
io_object_t ioObj = IO_OBJECT_NULL;
CFTypeRef regEntry = NULL;
errmsg = "allocation failure";
caches = calloc(1, sizeof(*caches));
if (!caches) goto finish;
caches->cachefd = -1; (void)strlcpy(caches->root, "<unknown>", sizeof(caches->root));
errmsg = "error extracting volume's root path";
if (!CFURLGetFileSystemRepresentation(volURL, false,
(UInt8*)caches->root, sizeof(caches->root))) {
goto finish;
}
errmsg = "error reading " kBootCachesPath;
pathcpy(bcpath, caches->root);
pathcat(bcpath, kBootCachesPath);
caches->cachefd = (errnum = open(bcpath, O_RDONLY|O_EVTONLY));
if (errnum == -1) {
if (errno == ENOENT) {
errmsg = NULL;
}
goto finish;
}
if (fstatfs(caches->cachefd, &rootsfs)) {
goto finish;
}
if (fstat(caches->cachefd, &sb)) {
goto finish;
}
caches->bcTime = sb.st_mtimespec; if (rootsfs.f_flags & MNT_QUARANTINE) {
errmsg = kBootCachesPath " quarantined";
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;
}
errmsg = "trouble reading " kBootCachesPath;
bcDict = copy_dict_from_fd(caches->cachefd, &sb);
if (!bcDict) goto finish;
errmsg = "error obtaining CoreStorage volume's UUID";
if (IO_OBJECT_NULL == (ioObj = DADiskCopyIOMedia(dadisk)))
goto finish;
regEntry = IORegistryEntryCreateCFProperty(ioObj,
CFSTR(kCoreStorageLVFUUIDKey), nil, 0);
if (regEntry && CFGetTypeID(regEntry) == CFStringGetTypeID()) {
caches->csfde_uuid = (CFStringRef)CFRetain(regEntry);
}
if (!(ddesc = DADiskCopyDescription(dadisk)))
goto finish;
if (extractProps(caches, bcDict, ddesc, &errmsg)) {
goto finish;
}
if (geteuid() == 0 && (rootsfs.f_flags & MNT_IGNORE_OWNERSHIP) == 0 &&
(rootsfs.f_flags & MNT_RDONLY) == 0) {
errmsg = NULL; errnum = createCacheDirs(caches);
if (errnum) 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);
}
errno = 0;
}
if (ddesc) CFRelease(ddesc);
if (bcDict) CFRelease(bcDict); if (regEntry) CFRelease(regEntry);
if (ioObj != IO_OBJECT_NULL)
IOObjectRelease(ioObj);
if (!rval) {
destroyCaches(caches); }
return rval;
}
struct bootCaches *
readBootCachesForVolURL(CFURLRef volumeURL)
{
struct bootCaches * result = NULL;
DASessionRef daSession = NULL; DADiskRef dadisk = NULL;
daSession = DASessionCreate(kCFAllocatorDefault);
if (!daSession) {
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"couldn't get description from Disk Arbitration");
goto finish;
}
dadisk =DADiskCreateFromVolumePath(kCFAllocatorDefault,daSession,volumeURL);
if (!dadisk) {
goto finish;
}
result = readCaches_internal(volumeURL, dadisk);
finish:
SAFE_RELEASE(dadisk);
SAFE_RELEASE(daSession);
return result;
}
struct bootCaches*
readBootCachesForDADisk(DADiskRef dadisk)
{
struct bootCaches *rval = NULL;
CFDictionaryRef ddesc = NULL;
CFURLRef volURL = NULL; int ntries = 0;
do {
ddesc = DADiskCopyDescription(dadisk);
if (!ddesc) goto finish;
volURL = CFDictionaryGetValue(ddesc,kDADiskDescriptionVolumePathKey);
if (volURL) {
break;
} else {
sleep(1);
CFRelease(ddesc);
ddesc = NULL;
}
} while (++ntries < kKextdDiskArbMaxRetries);
if (!volURL) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Disk description missing mount point for %d tries",
ntries);
goto finish;
}
if (ntries) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogFileAccessFlag,
"Warning: readCaches got mount point after %d tries.", ntries);
}
rval = readCaches_internal(volURL, dadisk);
finish:
if (ddesc) CFRelease(ddesc);
return rval;
}
Boolean needsUpdate(char *root, cachedPath* cpath)
{
Boolean outofdate = false;
Boolean rfpresent, tsvalid;
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) == 0) {
rfpresent = true;
} else if (errno == ENOENT) {
rfpresent = false;
} else {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Cached file %s: %s.", fullrp, strerror(errno));
goto finish;
}
if (rfpresent) {
TIMESPEC_TO_TIMEVAL(&cpath->tstamps[0], &rsb.st_atimespec);
TIMESPEC_TO_TIMEVAL(&cpath->tstamps[1], &rsb.st_ctimespec);
} else {
bzero(cpath->tstamps, sizeof(cpath->tstamps));
}
if (stat(fulltsp, &tsb) == 0) {
if (tsb.st_mtimespec.tv_sec != 0) {
tsvalid = true;
} else {
tsvalid = false;
}
} else if (errno == ENOENT) {
tsvalid = false;
} else {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"timestamp cache %s: %s!", fulltsp, strerror(errno));
goto finish;
}
if (rfpresent && tsvalid) {
outofdate = (tsb.st_mtimespec.tv_sec != rsb.st_ctimespec.tv_sec ||
tsb.st_mtimespec.tv_nsec != rsb.st_ctimespec.tv_nsec);
} else if (!rfpresent && tsvalid) {
outofdate = true;
} else if (rfpresent && !tsvalid) {
outofdate = true;
} else {
outofdate = false;
}
finish:
return outofdate;
}
Boolean needUpdates(struct bootCaches *caches, Boolean *rps, Boolean *booters,
Boolean *misc, OSKextLogSpec oodLogSpec)
{
Boolean rpsOOD, bootersOOD, miscOOD, anyOOD;
cachedPath *cp;
rpsOOD = bootersOOD = miscOOD = anyOOD = false;
for (cp = caches->rpspaths; cp < &caches->rpspaths[caches->nrps]; cp++) {
if (needsUpdate(caches->root, cp)) {
OSKextLog(NULL, oodLogSpec, "%s out of date.", cp->rpath);
anyOOD = rpsOOD = true;
}
}
if ((cp = &(caches->efibooter)), cp->rpath[0]) {
if (needsUpdate(caches->root, cp)) {
OSKextLog(NULL, oodLogSpec, "%s out of date.", cp->rpath);
anyOOD = bootersOOD = true;
}
}
if ((cp = &(caches->ofbooter)), cp->rpath[0]) {
if (needsUpdate(caches->root, cp)) {
OSKextLog(NULL, oodLogSpec, "%s out of date.", cp->rpath);
anyOOD = bootersOOD = true;
}
}
for (cp = caches->miscpaths; cp < &caches->miscpaths[caches->nmisc]; cp++) {
if (needsUpdate(caches->root, cp)) {
OSKextLog(NULL, oodLogSpec, "%s out of date.", cp->rpath);
anyOOD = miscOOD = true;
}
}
if (rps) *rps = rpsOOD;
if (booters) *booters = bootersOOD;
if (misc) *misc = miscOOD;
return anyOOD;
}
static int
_sutimes(int fdvol, char *path, int oflags, struct timeval times[2])
{
int bsderr;
int fd = -1;
if (-1 == (fd = sopen(fdvol, path, oflags, kCacheFileMode))) {
bsderr = fd;
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"sopen(%s): %s", path, strerror(errno));
goto finish;
}
if ((bsderr = futimes(fd, times))) {
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"futimes(<%s>): %s", path, strerror(errno));
}
finish:
if (fd == -1) close(fd);
return bsderr;
}
static int
updateStamp(char *root, cachedPath *cpath, int fdvol, int command)
{
int bsderr = -1;
char fulltspath[PATH_MAX];
pathcpy(fulltspath, root);
pathcat(fulltspath, cpath->tspath);
bsderr = sunlink(fdvol, fulltspath);
if (bsderr == -1 && errno == ENOENT) {
bsderr = 0;
}
if (command == kBCStampsApplyTimes) {
bsderr = _sutimes(fdvol, fulltspath, O_CREAT, cpath->tstamps);
}
finish:
return bsderr;
}
#define BRDBG_DISABLE_EXTSYNC_F "/var/db/.BRDisableExtraSync"
int
updateStamps(struct bootCaches *caches, int command)
{
int rval = 0;
cachedPath *cp;
struct stat sb;
switch (command) {
case kBCStampsApplyTimes:
case kBCStampsUnlinkOnly:
break;
default:
return EINVAL;
}
for (cp = caches->rpspaths; cp < &caches->rpspaths[caches->nrps]; cp++) {
rval |= updateStamp(caches->root, cp, caches->cachefd, command);
}
if ((cp = &(caches->efibooter)), cp->rpath[0]) {
rval |= updateStamp(caches->root, cp, caches->cachefd, command);
}
if ((cp = &(caches->ofbooter)), cp->rpath[0]) {
rval |= updateStamp(caches->root, cp, caches->cachefd, command);
}
for (cp = caches->miscpaths; cp < &caches->miscpaths[caches->nmisc]; cp++){
rval |= updateStamp(caches->root, cp, caches->cachefd, command);
}
if (stat(BRDBG_DISABLE_EXTSYNC_F, &sb) == -1) {
rval |= fcntl(caches->cachefd, F_FULLFSYNC);
}
return rval;
}
int rebuild_kext_boot_cache_file(
struct bootCaches *caches,
Boolean wait,
const char * kext_boot_cache_file,
const char * kernel_file)
{
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 * lastslash = NULL;
char rcpath[PATH_MAX] = "";
struct stat sb;
char full_cache_file_path[PATH_MAX] = "";
char full_cache_file_dir_path[PATH_MAX] = "";
char fullextsp[PATH_MAX] = "";
char fullkernelp[PATH_MAX] = "";
Boolean generateKernelcache = false;
int mkextVersion = 0;
if (!caches->kext_boot_cache_file
) {
goto finish;
}
pbDict = CFDictionaryGetValue(caches->cacheinfo, kBCPostBootKey);
if (!pbDict || CFGetTypeID(pbDict) != CFDictionaryGetTypeID()) goto finish;
do {
mkDict = CFDictionaryGetValue(pbDict, kBCKernelcacheV1Key);
if (mkDict) {
generateKernelcache = true;
break;
}
mkDict = CFDictionaryGetValue(pbDict, kBCMKext2Key);
if (mkDict) {
mkextVersion = 2;
break;
}
mkDict = CFDictionaryGetValue(pbDict, kBCMKextKey);
if (mkDict) {
mkextVersion = 1;
break;
}
} while (0);
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 + 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++] = "-arch";
kcargs[argi++] = archstrs[i];
}
kcargs[argi++] = "-local-root";
pathcpy(rcpath, caches->root);
removeTrailingSlashes(rcpath); pathcat(rcpath, "/etc/rc.cdrom");
if (stat(rcpath, &sb) == 0) {
kcargs[argi++] = "-network-root";
}
if (generateKernelcache) {
if (0 == strcmp(caches->root, "/")) {
kcargs[argi++] = "-all-loaded";
}
pathcpy(fullkernelp, caches->root);
removeTrailingSlashes(fullkernelp);
pathcat(fullkernelp, kernel_file);
kcargs[argi++] = "-kernel";
kcargs[argi++] = fullkernelp;
kcargs[argi++] = "-prelinked-kernel";
} else if (mkextVersion == 2) {
kcargs[argi++] = "-mkext2";
} else if (mkextVersion == 1) {
kcargs[argi++] = "-mkext1";
} else {
goto finish;
}
pathcpy(full_cache_file_path, caches->root);
removeTrailingSlashes(full_cache_file_path);
pathcat(full_cache_file_path, kext_boot_cache_file);
kcargs[argi++] = full_cache_file_path;
kcargs[argi++] = "-volume-root";
kcargs[argi++] = caches->root;
pathcpy(fullextsp, caches->root);
removeTrailingSlashes(fullextsp);
pathcat(fullextsp, caches->exts);
kcargs[argi++] = fullextsp;
kcargs[argi] = NULL;
pathcpy(full_cache_file_dir_path, full_cache_file_path);
lastslash = rindex(full_cache_file_dir_path, '/');
if (lastslash) {
*lastslash = '\0';
if ((rval = sdeepmkdir(caches->cachefd, full_cache_file_dir_path,
kCacheDirMode))) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"failed to create cache folder %s.", full_cache_file_dir_path);
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 plistCachesNeedRebuild(const NXArchInfo * kernelArchInfo)
{
Boolean result = true;
CFArrayRef systemExtensionsFolderURLs = NULL; CFStringRef cacheBasename = 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 (!_OSKextReadCache(directoryURL, CFSTR(_kOSKextIdentifierCacheBasename),
NULL, _kOSKextCacheFormatCFBinary, false,
NULL)) {
goto finish;
}
}
cacheBasename = CFStringCreateWithFormat(kCFAllocatorDefault,
NULL, CFSTR("%s%s"),
_kKextPropertyValuesCacheBasename,
"OSBundleHelper");
if (!cacheBasename) {
OSKextLogMemError();
result = false; goto finish;
}
if (!_OSKextReadCache(systemExtensionsFolderURLs, cacheBasename,
kernelArchInfo, _kOSKextCacheFormatCFXML, false,
NULL)) {
goto finish;
}
result = false;
finish:
SAFE_RELEASE(cacheBasename);
return result;
}
Boolean check_kext_boot_cache_file(
struct bootCaches * caches,
const char * cache_path,
const char * kernel_path)
{
Boolean needsrebuild = false;
char full_cache_file_path[PATH_MAX] = "";
char fullextsp[PATH_MAX] = "";
char fullkernelp[PATH_MAX] = "";
struct stat extsb;
struct stat kernelsb;
struct stat sb;
time_t validModtime;
if (cache_path) {
pathcpy(full_cache_file_path, caches->root);
removeTrailingSlashes(full_cache_file_path);
pathcat(full_cache_file_path, cache_path);
pathcpy(fullextsp, caches->root);
removeTrailingSlashes(fullextsp);
pathcat(fullextsp, caches->exts);
if (stat(fullextsp, &extsb) == -1) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogFileAccessFlag,
"Warning: %s: %s", fullextsp, strerror(errno));
goto finish;
}
validModtime = extsb.st_mtime + 1;
if (kernel_path[0]) {
pathcpy(fullkernelp, caches->root);
removeTrailingSlashes(fullkernelp);
pathcat(fullkernelp, kernel_path);
if (stat(fullkernelp, &kernelsb) == -1) {
OSKextLog( NULL,
kOSKextLogBasicLevel | kOSKextLogFileAccessFlag,
"Note: %s: %s", fullkernelp, strerror(errno));
goto finish;
}
if (kernelsb.st_mtime > extsb.st_mtime) {
validModtime = kernelsb.st_mtime + 1;
}
}
needsrebuild = true; if (stat(full_cache_file_path, &sb) == -1) {
goto finish;
}
needsrebuild = (sb.st_mtime != validModtime);
}
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;
}
static int
copy_csfde_props(CFStringRef uuidStr,
CFDictionaryRef *encContext,
int64_t *timeStamp)
{
int rval = ELAST + 1;
CFDictionaryRef lvfprops = NULL, ectx = NULL;
CFNumberRef propStampRef;
int64_t propStamp;
if (!uuidStr) goto finish;
if (CoreStorageCopyFamilyProperties == NULL) {
rval = ESHLIBVERS;
goto finish;
}
lvfprops = CoreStorageCopyFamilyProperties(uuidStr);
if (!lvfprops) goto finish;
ectx = (CFMutableDictionaryRef)CFDictionaryGetValue(lvfprops,
CFSTR(kCoreStorageFamilyEncryptionContextKey));
if (!ectx || CFGetTypeID(ectx) != CFDictionaryGetTypeID())
goto finish;
propStampRef = CFDictionaryGetValue(ectx, CFSTR(kCSFDELastUpdateTime));
if (propStampRef) {
if (CFGetTypeID(propStampRef) != CFNumberGetTypeID())
goto finish;
if (!CFNumberGetValue(propStampRef, kCFNumberSInt64Type, &propStamp))
goto finish;
} else {
propStamp = 0LL;
}
rval = 0;
if (encContext) *encContext = CFRetain(ectx);
if (timeStamp) *timeStamp = propStamp;
finish:
if (lvfprops) CFRelease(lvfprops);
return rval;
}
Boolean
check_csfde(struct bootCaches *caches)
{
Boolean encrypted, needsupdate = false;
CFDictionaryRef ectx = NULL;
CFArrayRef eusers;
int64_t propStamp, erStamp;
char erpath[PATH_MAX];
struct stat ersb;
if (caches->csfde_uuid == NULL || !caches->erpropcache)
goto finish;
if (copy_csfde_props(caches->csfde_uuid, &ectx, &propStamp))
goto finish;
eusers = (CFArrayRef)CFDictionaryGetValue(ectx, CFSTR(kCSFDECryptoUsersID));
encrypted = (eusers && CFArrayGetCount(eusers));
if (!encrypted)
goto finish;
pathcpy(erpath, caches->root);
pathcat(erpath, caches->erpropcache->rpath);
if (stat(erpath, &ersb) == 0) {
erStamp = ersb.st_mtimespec.tv_sec;
} else {
if (errno == ENOENT) {
erStamp = 0LL;
} else {
goto finish;
}
}
needsupdate = erStamp != propStamp;
finish:
if (ectx) CFRelease(ectx);
return needsupdate;
}
bool CSFDEWritePropertyCacheToFD(CFDictionaryRef context, int fd, CFStringRef wipeKeyUUID);
int
rebuild_csfde_cache(struct bootCaches *caches)
{
int rval = ELAST + 1;
CFDictionaryRef ectx = NULL;
int64_t propStamp;
char *errmsg = NULL;
char erpath[PATH_MAX];
int erfd = -1;
CFArrayRef dataVolumes = NULL;
CFStringRef wipeKeyUUID = NULL;
if (CoreStorageCopyPVWipeKeyUUID==NULL || CSFDEInitPropertyCache==NULL){
errmsg = "CoreStorage library symbols unavailable";
errno = ESHLIBVERS;
goto finish;
}
(void)hasBootRootBoots(caches, NULL, &dataVolumes, NULL);
if (!dataVolumes) {
errmsg = "no data partitions! (for Wipe Key)";
goto finish;
}
CFIndex count = CFArrayGetCount(dataVolumes);
if (count > 1) {
errmsg = "more than one physical volume encountered";
goto finish;
} else if (count == 0) {
errmsg = "no data partitions! (for Wipe Key)";
goto finish;
}
CFStringRef physicalVolume = CFArrayGetValueAtIndex(dataVolumes, 0);
char bsdName[DEVMAXPATHSIZE];
if (!CFStringGetCString(physicalVolume, bsdName, sizeof(bsdName), kCFStringEncodingUTF8)) {
errmsg = "String conversion failure for bsdName.";
goto finish;
}
wipeKeyUUID = CoreStorageCopyPVWipeKeyUUID(bsdName);
if (!wipeKeyUUID) {
errmsg = "error getting volume wipe key";
goto finish;
}
errmsg = "error getting encryption context data";
if (!caches->csfde_uuid || !caches->erpropcache)
goto finish;
if (copy_csfde_props(caches->csfde_uuid, &ectx, &propStamp))
goto finish;
errmsg = "error building encryption context cache file path";
pathcpy(erpath, caches->root);
pathcat(erpath, caches->erpropcache->rpath);
errmsg = NULL;
if (szerofile(caches->cachefd, erpath)) {
OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag,
"%s: %s", erpath, strerror(errno));
}
(void)sunlink(caches->cachefd, erpath);
if(-1==(erfd=sopen(caches->cachefd,erpath,O_CREAT|O_RDWR,kCacheFileMode))){
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s: %s", erpath, strerror(errno));
rval = errno;
goto finish;
}
if (!CSFDEWritePropertyCacheToFD(ectx, erfd, wipeKeyUUID)) {
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"CSFDEWritePropertyCacheToFD(%s) failed", erpath);
goto finish;
}
errmsg = NULL;
rval = 0;
finish:
if (erfd != -1)
close (erfd);
if (dataVolumes)
CFRelease(dataVolumes);
if (wipeKeyUUID)
CFRelease(wipeKeyUUID);
if (ectx)
CFRelease(ectx);
if (errmsg) {
if (rval == -1) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s: %s", errmsg, strerror(errno));
} else {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s: %s", caches->root, errmsg);
}
}
return rval;
}
static int
get_locres_info(struct bootCaches *caches, char locRsrcDir[PATH_MAX],
char prefPath[PATH_MAX], struct stat *prefsb,
char locCacheDir[PATH_MAX], time_t *validModTime)
{
int rval = EOVERFLOW; time_t newestTime;
struct stat sb;
if (!validModTime) {
rval = EINVAL;
goto finish;
}
pathcpy(locRsrcDir, caches->root);
pathcat(locRsrcDir, caches->locSource);
if (stat(locRsrcDir, &sb)) {
OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag,
"%s: %s", locRsrcDir, strerror(errno));
rval = errno;
goto finish;
}
newestTime = sb.st_mtime;
pathcpy(prefPath, caches->root);
pathcat(prefPath, caches->locPref);
if (stat(prefPath, prefsb) == 0) {
if (prefsb->st_mtime > newestTime) {
newestTime = prefsb->st_mtime;
}
} else {
if (errno != ENOENT) {
OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag,
"%s: %s", prefPath, strerror(errno));
rval = errno;
goto finish;
}
}
*validModTime = newestTime + 1;
pathcpy(locCacheDir, caches->root);
pathcat(locCacheDir, caches->efiloccache->rpath);
rval = 0;
finish:
if (rval == EOVERFLOW) {
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"get_locres_info(%s): %s", caches->root, strerror(rval));
}
return rval;
}
Boolean
check_loccache(struct bootCaches *caches)
{
Boolean needsupdate = false;
struct stat prefsb, cachesb;
char locRsrcDir[PATH_MAX], prefPath[PATH_MAX];
char locCacheDir[PATH_MAX];
time_t validModTime;
if (!caches->efiloccache) {
goto finish;
}
if (get_locres_info(caches, locRsrcDir, prefPath, &prefsb,
locCacheDir, &validModTime)) {
goto finish;
}
if (stat(locCacheDir, &cachesb) == 0) {
needsupdate = (cachesb.st_mtime != validModTime);
} else if (errno == ENOENT) {
needsupdate = true;
}
finish:
return needsupdate;
}
struct writeRsrcCtx {
struct bootCaches *caches;
char *locCacheDir;
int *result;
};
void
_writeResource(const void *value, void *ctxp)
{
int bsderr;
CFDictionaryRef rsrc = (CFDictionaryRef)value;
struct writeRsrcCtx *ctx = (struct writeRsrcCtx*)ctxp;
struct bootCaches *caches = ctx->caches;
CFDataRef data;
void *buf;
ssize_t bufsz;
CFStringRef nameStr;
int fflags, fd = -1;
char fname[PATH_MAX], fullp[PATH_MAX];
bsderr = EFTYPE;
if (!(data = CFDictionaryGetValue(rsrc, kEFILoginDataKey)))
goto finish;
if (!(buf = (void*)CFDataGetBytePtr(data)))
goto finish;
bufsz = (ssize_t)CFDataGetLength(data);
if (bufsz < 0) goto finish;
if (!(nameStr = CFDictionaryGetValue(rsrc, kEFILoginFileNameKey)))
goto finish;
if(!CFStringGetFileSystemRepresentation(nameStr, fname, PATH_MAX))
goto finish;
bsderr = EOVERFLOW;
pathcpy(fullp, ctx->locCacheDir);
pathcat(fullp, "/");
pathcat(fullp, fname);
fflags = O_WRONLY | O_CREAT | O_TRUNC; if (-1 == (fd = sopen(caches->cachefd, fullp, fflags, kCacheFileMode))) {
bsderr = -1;
goto finish;
}
if (write(fd, buf, bufsz) != bufsz) {
bsderr = -1;
goto finish;
}
bsderr = 0;
finish:
if (bsderr) {
*(ctx->result) = bsderr;
}
if (fd != -1) close(fd);
return;
}
#define LANGSKEY CFSTR("AppleLanguages") // key in .GlobalPreferences
#define ENGLISHKEY CFSTR("en")
static int
_writeEFILoginResources(struct bootCaches *caches,
char prefPath[PATH_MAX], struct stat *prefsb,
char locCacheDir[PATH_MAX])
{
int result; int gpfd = -1;
CFDictionaryRef gprefs = NULL;
CFMutableArrayRef locsList = NULL; CFStringRef volStr = NULL;
CFArrayRef blobList = NULL;
CFRange allEntries;
struct writeRsrcCtx applyCtx = { caches, locCacheDir, &result };
if (EFILoginCopyInterfaceGraphics == NULL) {
result = ESHLIBVERS;
goto finish;
}
if ((gpfd = sopen(caches->cachefd, prefPath, O_RDONLY, 0)) >= 0 &&
(gprefs = copy_dict_from_fd(gpfd, prefsb)) &&
(locsList=(CFMutableArrayRef)CFDictionaryGetValue(gprefs,LANGSKEY)) &&
CFGetTypeID(locsList) == CFArrayGetTypeID()) {
CFRetain(locsList);
} else {
CFRange range = { 0, 1 };
locsList = CFArrayCreateMutable(nil, 1, &kCFTypeArrayCallBacks);
if (!locsList) goto finish;
CFArrayAppendValue(locsList, ENGLISHKEY);
if (!CFArrayContainsValue(locsList, range, ENGLISHKEY)) goto finish;
}
volStr = CFStringCreateWithFileSystemRepresentation(nil, caches->root);
if (!volStr ||
!(blobList = EFILoginCopyInterfaceGraphics(locsList, volStr))) {
result = ENOMEM;
goto finish;
}
result = 0; allEntries = CFRangeMake(0, CFArrayGetCount(blobList));
CFArrayApplyFunction(blobList, allEntries, _writeResource, &applyCtx);
if (result) goto finish;
result = 0;
finish:
if (blobList) CFRelease(blobList);
if (volStr) CFRelease(volStr);
if (locsList) CFRelease(locsList);
if (gprefs) CFRelease(gprefs);
if (gpfd != -1) close(gpfd);
return result;
}
int
rebuild_loccache(struct bootCaches *caches)
{
int result;
struct stat cachesb, prefsb;
char locRsrcDir[PATH_MAX], prefPath[PATH_MAX];
char locCacheDir[PATH_MAX];
time_t validModTime;
int fd = -1;
struct timeval times[2];
if ((result = get_locres_info(caches, locRsrcDir, prefPath, &prefsb,
locCacheDir, &validModTime))) {
goto finish;;
}
(void)sdeepunlink(caches->cachefd, locCacheDir);
if ((result = sdeepmkdir(caches->cachefd,locCacheDir,kCacheDirMode)))
goto finish;
result = _writeEFILoginResources(caches, prefPath, &prefsb, locCacheDir);
if (result) {
OSKextLog(NULL, kOSKextLogErrorLevel|kOSKextLogFileAccessFlag,
"_writeEFILoginResources() failed: %d",
(result == -1) ? errno : result);
(void)sdeepunlink(caches->cachefd, locCacheDir);
goto finish;
}
if ((result = stat(locCacheDir, &cachesb))) {
OSKextLog(NULL, kOSKextLogWarningLevel|kOSKextLogFileAccessFlag,
"%s: %s", locCacheDir, strerror(errno));
goto finish;
}
cachesb.st_mtime = validModTime;
TIMESPEC_TO_TIMEVAL(×[0], &cachesb.st_atimespec);
TIMESPEC_TO_TIMEVAL(×[1], &cachesb.st_mtimespec);
result = _sutimes(caches->cachefd, locCacheDir, O_RDONLY, times);
finish:
if (fd != -1) close(fd);
return result;
}
Boolean
hasBootRootBoots(struct bootCaches *caches, CFArrayRef *auxPartsCopy,
CFArrayRef *dataPartsCopy, Boolean *isAPM)
{
CFDictionaryRef binfo = NULL;
Boolean rval = false, apm = false;
CFArrayRef dparts = NULL, bparts = NULL;
char * errmsg = NULL;
char stack_bsdname[DEVMAXPATHSIZE];
char * lookup_bsdname = caches->bsdname;
CFArrayRef dataPartitions = NULL; size_t fullLen;
char fulldev[DEVMAXPATHSIZE];
#if BLESS_BUG_RESOLVED
char parentdevname[DEVMAXPATHSIZE];
uint32_t partitionNum;
BLPartitionType partitionType;
#endif
if (BLCreateBooterInformationDictionary(NULL, lookup_bsdname, &binfo))
goto finish;
bparts = CFDictionaryGetValue(binfo, kBLAuxiliaryPartitionsKey);
dparts = CFDictionaryGetValue(binfo, kBLDataPartitionsKey);
if (!bparts || !dparts) goto finish;
dataPartitions = CFDictionaryGetValue(binfo, kBLDataPartitionsKey);
if (dataPartitions && CFArrayGetCount(dataPartitions)) {
CFStringRef dpBsdName = CFArrayGetValueAtIndex(dataPartitions, 0);
if (dpBsdName) {
errmsg = "String conversion failure for bsdname.";
if (!CFStringGetFileSystemRepresentation(dpBsdName, stack_bsdname,
sizeof(stack_bsdname)))
goto finish;
lookup_bsdname = stack_bsdname;
}
}
errmsg = "Internal error.";
fullLen = snprintf(fulldev, sizeof(fulldev), "/dev/%s", lookup_bsdname);
if (fullLen >= sizeof(fulldev)) {
goto finish;
}
#if BLESS_BUG_RESOLVED
errmsg = "Can't get partition type.";
if (BLGetParentDeviceAndPartitionType(NULL ,
fulldevname, parentdevname, &partitionNum, &partitionType))
{
goto finish;
}
if (partitionType == kBLPartitionType_APM) {
apm = true;
}
#endif
if (apm) {
CFDictionaryRef pbDict, mk2Dict, kcDict;
errmsg = NULL;
pbDict = CFDictionaryGetValue(caches->cacheinfo, kBCPostBootKey);
if (!pbDict || CFGetTypeID(pbDict) != CFDictionaryGetTypeID()) goto finish;
kcDict = CFDictionaryGetValue(pbDict, kBCKernelcacheV1Key);
mk2Dict = CFDictionaryGetValue(pbDict, kBCMKext2Key);
if (!kcDict && !mk2Dict && caches->ofbooter.rpath[0] == '\0')
goto finish;
}
rval = (CFArrayGetCount(bparts) > 0);
errmsg = NULL;
finish:
if (auxPartsCopy) {
if (bparts) CFRetain(bparts);
*auxPartsCopy = bparts;
}
if (dataPartsCopy) {
if (dparts) CFRetain(dparts);
*dataPartsCopy = dparts;
}
if (isAPM) *isAPM = apm;
if (binfo) CFRelease(binfo);
if (errmsg) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s: %s", caches->root, errmsg);
}
return rval;
}
CFArrayRef
BRCopyActiveBootPartitions(CFURLRef volRoot)
{
CFArrayRef rval = NULL;
char path[PATH_MAX], *bsdname;
struct statfs sfs;
CFDictionaryRef binfo = NULL;
if (!volRoot) goto finish;
if (!CFURLGetFileSystemRepresentation(
volRoot, false, (UInt8*)path, sizeof(path))) {
goto finish;
}
if (-1 == statfs(path, &sfs)) goto finish;
if (strlen(sfs.f_mntfromname) < sizeof(_PATH_DEV)) {
goto finish;
}
bsdname = sfs.f_mntfromname + (sizeof(_PATH_DEV)-1);
if (BLCreateBooterInformationDictionary(NULL, bsdname, &binfo))
goto finish;
rval = CFDictionaryGetValue(binfo, kBLAuxiliaryPartitionsKey);
if (rval) CFRetain(rval);
finish:
if (binfo) CFRelease(binfo);
return rval;
}
void _daDone(DADiskRef disk __unused, DADissenterRef dissenter, void *ctx)
{
if (dissenter)
CFRetain(dissenter);
*(DADissenterRef*)ctx = dissenter;
CFRunLoopStop(CFRunLoopGetCurrent()); }
static void removeTrailingSlashes(char * path)
{
size_t pathLength = strlen(path);
size_t scanIndex = pathLength - 1;
if (!pathLength) return;
while (scanIndex && path[scanIndex] == '/') {
path[scanIndex--] = '\0';
}
return;
}
int
updateMount(mountpoint_t mount, uint32_t mntgoal)
{
int result = ELAST + 1;
DASessionRef session = NULL;
CFStringRef toggleMode = CFSTR("updateMountMode");
CFURLRef volURL = NULL;
DADiskRef disk = NULL;
DADissenterRef dis = (void*)kCFNull;
CFStringRef mountargs[] = {
CFSTR("update"),
( mntgoal & MNT_NODEV ) ? CFSTR("nodev") : CFSTR("dev"),
( mntgoal & MNT_NOEXEC ) ? CFSTR("noexec") : CFSTR("exec"),
( mntgoal & MNT_NOSUID ) ? CFSTR("nosuid") : CFSTR("suid"),
( mntgoal & MNT_RDONLY ) ? CFSTR("rdonly") : CFSTR("rw"),
( mntgoal & MNT_DONTBROWSE ) ? CFSTR("nobrowse") : CFSTR("browse"),
(mntgoal & MNT_IGNORE_OWNERSHIP) ? CFSTR("noowners") : CFSTR("owners"),
NULL };
if (!(session = DASessionCreate(nil))) goto finish;
DASessionScheduleWithRunLoop(session, CFRunLoopGetCurrent(), toggleMode);
volURL=CFURLCreateFromFileSystemRepresentation(nil,(void*)mount,MNAMELEN,1);
if (!(volURL)) goto finish;
if (!(disk = DADiskCreateFromVolumePath(nil, session, volURL))) goto finish;
DADiskMountWithArguments(disk, NULL, kDADiskMountOptionDefault, _daDone,
&dis, mountargs);
while (dis == (void*)kCFNull) {
CFRunLoopRunInMode(toggleMode, 0, true); }
if (dis) goto finish;
result = 0;
finish:
if (dis && dis != (void*)kCFNull) CFRelease(dis);
if (disk) CFRelease(disk);
if (session) CFRelease(session);
if (volURL) CFRelease(volURL);
if (result) {
OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag,
"Warning: couldn't update %s's mount to %0x", mount, mntgoal);
}
return result;
}
pid_t launch_rebuild_all(char * rootPath, Boolean force, Boolean wait)
{
pid_t rval = -1;
int argc, argi = 0;
char **kcargs = NULL;
argc = 1 + 1 + 1 + 1 + (force == true) + 1;
kcargs = malloc(argc * sizeof(char*));
if (!kcargs) goto finish;
kcargs[argi++] = "/usr/sbin/kextcache";
kcargs[argi++] = "-F"; if (force) {
kcargs[argi++] = "-f";
}
kcargs[argi++] = "-u";
kcargs[argi++] = rootPath;
kcargs[argi] = NULL;
rval = fork_program(kcargs[0], kcargs, wait);
finish:
if (kcargs) free(kcargs);
if (rval < 0)
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Error launching kextcache -u.");
return rval;
}
#define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;}
int
copyVolumeUUIDs(const char *volPath, uuid_t vol_uuid, CFStringRef *cslvf_uuid __unused)
{
int rval = ENODEV;
DADiskRef dadisk = NULL;
CFDictionaryRef dadesc = NULL;
CFUUIDRef volUUID; CFUUIDBytes uuidBytes;
COMPILE_TIME_ASSERT(sizeof(CFUUIDBytes) == sizeof(uuid_t));
CFTypeRef regEntry = NULL;
dadisk = createDiskForMount(NULL, volPath);
if (!dadisk) {
if (errno) rval = errno;
goto finish;
}
dadesc = DADiskCopyDescription(dadisk);
if (!dadesc) goto finish;
volUUID = CFDictionaryGetValue(dadesc, kDADiskDescriptionVolumeUUIDKey);
if (!volUUID) goto finish;
uuidBytes = CFUUIDGetUUIDBytes(volUUID);
memcpy(vol_uuid, &uuidBytes.byte0, sizeof(uuid_t));
rval = 0;
finish:
if (regEntry) CFRelease(regEntry);
if (dadesc) CFRelease(dadesc);
if (dadisk) CFRelease(dadisk);
return rval;
}