#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/attr.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 <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)
#define LOGERRxlate(ctx1, ctx2, val) do { \
char *c2cpy = ctx2, ctx[80]; \
if (ctx2 != NULL) { \
snprintf(ctx, 80, "%s: %s", ctx1, c2cpy); \
} else { \
snprintf(ctx, 80, "%s", ctx1); \
} \
if (val == -1) { \
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, \
"%s: %s", ctx, strerror(errno)); \
val = errno; \
} else { \
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, \
"%s: %s", ctx, strerror(val)); \
} \
} 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)
{
int rval = ENODEV;
CFDictionaryRef dict; CFIndex keyCount; CFIndex rpsindex = 0; CFStringRef str; CFStringRef createdStr = NULL;
rval = EFTYPE;
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,kBCCSFDELocalizationSrcKey)) {
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) {
CFNumberRef boolRef;
keyCount += CFDictionaryGetCount(erDict);
str = CFDictionaryGetValue(erDict, kBCCSFDEPropertyCacheKey);
if (str) {
MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str);
caches->erpropcache = &caches->rpspaths[rpsindex++];
keyCount--;
}
boolRef = CFDictionaryGetValue(erDict,kBCCSFDERootVolPropCacheKey);
if (boolRef) {
if (CFGetTypeID(boolRef) == CFBooleanGetTypeID()) {
caches->erpropTSOnly = CFEqual(boolRef, kCFBooleanFalse);
keyCount--;
} else {
goto finish;
}
}
str = CFDictionaryGetValue(erDict, kBCCSFDEDefResourcesDirKey);
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,kBCCSFDELocRsrcsCacheKey);
if (str) {
MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str);
caches->efiloccache = &caches->rpspaths[rpsindex++];
keyCount--;
str = CFDictionaryGetValue(erDict, kBCCSFDELocalizationSrcKey);
if (str && CFGetTypeID(str) == CFStringGetTypeID() &&
CFStringGetFileSystemRepresentation(str,
caches->locSource, sizeof(caches->locSource))) {
keyCount--;
} else {
goto finish;
}
str = CFDictionaryGetValue(erDict, kBCCSFDELanguagesPrefKey);
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 (CFDictionaryContainsKey(dict, kBCKernelcacheV2Key)) kcacheKeys++;
if (kcacheKeys > 1) {
goto finish;
}
do {
mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCKernelcacheV1Key);
if (!mkDict) {
mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCKernelcacheV2Key);
}
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 == 0 && (unsigned)rpsindex == caches->nrps) {
rval = 0;
caches->cacheinfo = CFRetain(bcDict); }
finish:
if (createdStr) CFRelease(createdStr);
return rval;
}
static int
createCacheDirs(struct bootCaches *caches)
{
int errnum, result = ELAST + 1;
struct statfs sfs;
char *errname;
struct stat sb;
char cachedir[PATH_MAX], uuiddir[PATH_MAX];
if (statfs(caches->root, &sfs) == 0) {
if (sfs.f_flags & MNT_IGNORE_OWNERSHIP) {
result = ENOTSUP; goto finish;
}
} else {
result = errno; goto finish;
}
errname = 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))){
result = errnum; goto finish;
}
} else {
result = errnum; goto finish;
}
}
if (caches->erpropcache) {
errname = caches->erpropcache->rpath;
pathcpy(cachedir, caches->root);
pathcat(cachedir, dirname(caches->erpropcache->rpath));
errname = cachedir;
if ((-1 == stat(cachedir, &sb))) {
if (errno == ENOENT) {
errnum=sdeepmkdir(caches->cachefd,cachedir,kCacheDirMode);
if (errnum) {
result = errnum; goto finish;
}
} else {
result = errno; goto finish;
}
}
}
errname = NULL;
result = 0;
finish:
if (result) {
LOGERRxlate(errname, NULL, result);
errno = 0;
}
return result;
}
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;
}
struct bootCaches*
readBootCaches(char *volRoot, BRUpdateOpts_t opts)
{
struct bootCaches *rval = NULL, *caches = NULL;
int errnum = ELAST + 1;
char *errmsg;
struct statfs rootsfs;
struct stat sb;
char bcpath[PATH_MAX];
CFDictionaryRef bcDict = NULL;
uuid_t vol_uuid;
char *vol_bsd, *vol_name;
errmsg = "allocation failure";
caches = calloc(1, sizeof(*caches));
if (!caches) goto finish;
caches->cachefd = -1; pathcpy(caches->root, volRoot);
errmsg = "error opening " 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 = "error obtaining storage information";
vol_bsd = caches->bsdname;
vol_name = caches->volname;
if ((errnum = copyVolumeInfo(volRoot, &vol_uuid, &caches->csfde_uuid,
&vol_bsd, &vol_name))){
errno = errnum; goto finish;
}
if ((opts & kBRUAnyBootStamps) == 0) {
uuid_unparse_upper(vol_uuid, caches->fsys_uuid);
}
errmsg = "error reading " kBootCachesPath;
bcDict = copy_dict_from_fd(caches->cachefd, &sb);
if (!bcDict) goto finish;
errmsg = NULL;
if ((errnum = extractProps(caches, bcDict))) {
errno = errnum; goto finish;
}
rval = caches;
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 (bcDict) CFRelease(bcDict);
if (!rval) {
destroyCaches(caches); }
return rval;
}
struct bootCaches*
readBootCachesForDADisk(DADiskRef dadisk)
{
struct bootCaches *rval = NULL;
CFDictionaryRef ddesc = NULL;
CFURLRef volURL; char volRoot[PATH_MAX];
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);
}
if (!CFURLGetFileSystemRepresentation(volURL, true,
(UInt8 *)volRoot, sizeof(volRoot))){
OSKextLogStringError(NULL);
goto finish;
}
rval = readBootCaches(volRoot, kBRUOptsNone);
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;
}
#define OODMSG "not cached."
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 " OODMSG, cp->rpath);
anyOOD = rpsOOD = true;
}
}
if ((cp = &(caches->efibooter)), cp->rpath[0]) {
if (needsUpdate(caches->root, cp)) {
OSKextLog(NULL, oodLogSpec, "%s " OODMSG, cp->rpath);
anyOOD = bootersOOD = true;
}
}
if ((cp = &(caches->ofbooter)), cp->rpath[0]) {
if (needsUpdate(caches->root, cp)) {
OSKextLog(NULL, oodLogSpec, "%s " OODMSG, cp->rpath);
anyOOD = bootersOOD = true;
}
}
for (cp = caches->miscpaths; cp < &caches->miscpaths[caches->nmisc]; cp++) {
if (needsUpdate(caches->root, cp)) {
OSKextLog(NULL, oodLogSpec, "%s " OODMSG, 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,
"%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 anyErr = 0; struct statfs sfs;
cachedPath *cp;
struct stat sb;
if (statfs(caches->root, &sfs) == 0) {
if ((sfs.f_flags & MNT_RDONLY)) {
OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag,
"Warning: %s read-only: no bootstamp updates",
caches->root);
return 0; }
}
switch (command) {
case kBCStampsApplyTimes:
case kBCStampsUnlinkOnly:
break;
default:
return EINVAL;
}
if (command == kBCStampsApplyTimes &&
(anyErr = createCacheDirs(caches))) {
return anyErr;
}
for (cp = caches->rpspaths; cp < &caches->rpspaths[caches->nrps]; cp++) {
anyErr |= updateStamp(caches->root, cp, caches->cachefd, command);
}
if ((cp = &(caches->efibooter)), cp->rpath[0]) {
anyErr |= updateStamp(caches->root, cp, caches->cachefd, command);
}
if ((cp = &(caches->ofbooter)), cp->rpath[0]) {
anyErr |= updateStamp(caches->root, cp, caches->cachefd, command);
}
for (cp = caches->miscpaths; cp < &caches->miscpaths[caches->nmisc]; cp++){
anyErr |= updateStamp(caches->root, cp, caches->cachefd, command);
}
if (stat(BRDBG_DISABLE_EXTSYNC_F, &sb) == -1) {
anyErr |= fcntl(caches->cachefd, F_FULLFSYNC);
}
return anyErr;
}
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)
mkDict = CFDictionaryGetValue(pbDict, kBCKernelcacheV2Key);
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;
}
int
copyCSFDEInfo(CFStringRef uuidStr, CFDictionaryRef *econtext,
time_t *timeStamp)
{
int rval = ELAST+1;
CFDictionaryRef lvfprops = NULL;
CFDictionaryRef ectx;
CFNumberRef psRef;
CFArrayRef eusers; Boolean encrypted;
if (!uuidStr) {
rval = EINVAL; goto finish;
}
if (CoreStorageCopyFamilyProperties == NULL) {
rval = ESHLIBVERS; goto finish;
}
lvfprops = CoreStorageCopyFamilyProperties(uuidStr);
if (!lvfprops) {
rval = EFTYPE; goto finish;
}
ectx = (CFMutableDictionaryRef)CFDictionaryGetValue(lvfprops,
CFSTR(kCoreStorageFamilyEncryptionContextKey));
if (!ectx || CFGetTypeID(ectx) != CFDictionaryGetTypeID()) {
rval = EFTYPE; goto finish;
}
eusers = (CFArrayRef)CFDictionaryGetValue(ectx, CFSTR(kCSFDECryptoUsersID));
encrypted = (eusers && CFArrayGetCount(eusers));
if (encrypted) {
if (econtext) {
*econtext = CFRetain(ectx);
}
if (timeStamp) {
psRef = CFDictionaryGetValue(ectx, CFSTR(kCSFDELastUpdateTime));
if (psRef) {
if (CFGetTypeID(psRef) != CFNumberGetTypeID() ||
!CFNumberGetValue(psRef,kCFNumberSInt64Type,timeStamp)){
rval = EFTYPE; goto finish;
}
} else { *timeStamp = 0LL;
}
}
} else { if (econtext) *econtext = NULL;
if (timeStamp) *timeStamp = 0LL;
}
rval = 0;
finish:
if (lvfprops) CFRelease(lvfprops);
if (rval) {
OSKextLogCFString(NULL, kOSKextLogErrorLevel|kOSKextLogFileAccessFlag,
CFSTR("could not copy LVF props for %@: %s"),
uuidStr, strerror(rval));
}
return rval;
}
Boolean
check_csfde(struct bootCaches *caches)
{
Boolean needsupdate = false;
time_t propStamp, erStamp;
char erpath[PATH_MAX];
struct stat ersb;
if (!caches->csfde_uuid || !caches->erpropcache)
goto finish;
if (copyCSFDEInfo(caches->csfde_uuid, NULL, &propStamp))
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:
return needsupdate;
}
#define kOrigInitCookieDir "/System/Library/CoreServices"
#define kOrigInitCookieFile "/SystemVersion.plist"
static int
_writeCSFDENoFD(int scopefd, CFDictionaryRef ectx,
CFStringRef wipeKeyUUID, char *dstpath)
{
int bsderr, rval = ELAST + 1; int fd = -1;
Boolean createdCookie = false;
char parentpath[PATH_MAX], cookiepath[PATH_MAX];
char *relpath;
struct stat sb;
pathcpy(parentpath, dstpath);
relpath = strstr(parentpath, kCSFDEPropertyCacheFileEncrypted);
if (!relpath) {
rval = ESHLIBVERS; goto finish;
}
relpath[0] = '\0';
pathcpy(cookiepath, parentpath);
pathcat(cookiepath, kOrigInitCookieDir);
if ((bsderr = sdeepmkdir(scopefd, cookiepath, kCacheDirMode))) {
rval = bsderr; goto finish;
}
pathcat(cookiepath, kOrigInitCookieFile);
if (0 != stat(cookiepath, &sb)) {
if (0>=(fd = sopen(scopefd, cookiepath, O_CREAT, kCacheFileMode))) {
rval = errno; goto finish;
}
close(fd);
createdCookie = true;
}
errno = 0;
OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag,
"WARNING: no CSFDEWritePropertyCacheToFD(); "
"trying CSFDEInitPropertyCache()");
if (false == CSFDEInitPropertyCache(ectx, parentpath, wipeKeyUUID)) {
rval = ELAST + 1; goto finish;
}
if (-1 == stat(dstpath, &sb)) {
rval = errno; goto finish;
}
rval = 0;
finish:
if (createdCookie) {
(void)sunlink(scopefd, cookiepath); }
return rval;
}
int
writeCSFDEProps(int scopefd, CFDictionaryRef ectx,
char *cspvbsd, char *dstpath)
{
int errnum, rval = ELAST + 1;
CFStringRef wipeKeyUUID = NULL;
char dstparent[PATH_MAX];
int erfd = -1;
if (CoreStorageCopyPVWipeKeyUUID==NULL) {
rval = ESHLIBVERS; goto finish;
}
wipeKeyUUID = CoreStorageCopyPVWipeKeyUUID(cspvbsd);
if (!wipeKeyUUID) {
rval = ENODEV; goto finish;
}
if ((errnum = szerofile(scopefd, dstpath)) ||
((errnum = sunlink(scopefd, dstpath)) && errno != ENOENT)) {
OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag,
"WARNING: %s: %s", dstpath, strerror(errno));
}
if (strlcpy(dstparent,dirname(dstpath),PATH_MAX) >= PATH_MAX) {
rval = EOVERFLOW; goto finish;
}
errnum = sdeepmkdir(scopefd, dstparent, kCacheDirMode);
if (errnum) {
rval = errnum; goto finish;
}
if (CSFDEWritePropertyCacheToFD!=NULL) {
erfd = sopen(scopefd, dstpath, O_CREAT|O_RDWR, kCacheFileMode);
if (-1 == erfd) {
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s: %s", dstpath, strerror(errno));
rval = errno; goto finish;
}
if (!CSFDEWritePropertyCacheToFD(ectx, erfd, wipeKeyUUID)) {
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"CSFDEWritePropertyCacheToFD(%s) failed", dstpath);
rval = ELAST + 1; goto finish;
}
} else {
if ((errnum = _writeCSFDENoFD(scopefd,ectx,wipeKeyUUID,dstpath))) {
rval = errnum; goto finish;
}
}
rval = 0;
finish:
if (wipeKeyUUID) CFRelease(wipeKeyUUID);
if (erfd != -1) close(erfd);
if (rval == -1) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s: %s", dstpath, strerror(errno));
} else if (rval) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s: %s", dstpath, strerror(rval));
}
return rval;
}
static int
_writeLegacyCSFDECache(struct bootCaches *caches)
{
int errnum, rval = ELAST + 1;
CFArrayRef dataVolumes = NULL;
CFStringRef bsdStr; char bsdname[DEVMAXPATHSIZE];
CFDictionaryRef ectx = NULL;
char *errmsg = NULL;
char erpath[PATH_MAX];
int erfd = -1;
if (!caches->csfde_uuid || !caches->erpropcache) {
rval = EINVAL; goto finish;
}
(void)hasBootRootBoots(caches, NULL, &dataVolumes, NULL);
if (!dataVolumes || CFArrayGetCount(dataVolumes) == 0) {
errmsg = "no data partition! (for wipe key)";
rval = ENODEV; goto finish;
}
errmsg = "error getting volume wipe key";
bsdStr = CFArrayGetValueAtIndex(dataVolumes, 0);
if (!bsdStr) {
rval = ENODEV; goto finish;
}
if (!CFStringGetFileSystemRepresentation(bsdStr,bsdname,sizeof(bsdname))){
rval = EINVAL; goto finish;
}
errmsg = "error getting encryption context data";
if ((errnum = copyCSFDEInfo(caches->csfde_uuid, &ectx, NULL))) {
rval = errnum; goto finish;
}
errmsg = "error building encryption context cache file path";
pathcpy(erpath, caches->root);
pathcat(erpath, caches->erpropcache->rpath);
errmsg = NULL;
if (!ectx) {
(void)sunlink(caches->cachefd, erpath);
rval = 0; goto finish;
}
if ((errnum = writeCSFDEProps(caches->cachefd, ectx, bsdname, erpath))) {
rval = errnum; goto finish;
}
errmsg = NULL;
rval = 0;
finish:
if (erfd != -1) close (erfd);
if (dataVolumes) CFRelease(dataVolumes);
if (ectx) CFRelease(ectx);
if (rval)
LOGERRxlate(caches->root, errmsg, rval);
return rval;
}
int
rebuild_csfde_cache(struct bootCaches *caches)
{
int errnum, rval = ELAST + 1;
time_t timeStamp;
char erpath[PATH_MAX] = "<unknown>";
struct timeval times[2] = {{ 0, 0 }, { 0, 0 }};
if (!caches->csfde_uuid || !caches->erpropcache) {
rval = EINVAL; goto finish;
}
if ((errnum = createCacheDirs(caches))) {
rval = errnum; goto finish;
}
if (caches->erpropTSOnly == false) {
return _writeLegacyCSFDECache(caches); }
if ((errnum = copyCSFDEInfo(caches->csfde_uuid, NULL, &timeStamp))) {
rval = errnum; goto finish;
}
times[0].tv_sec = (__darwin_time_t)timeStamp;
times[1].tv_sec = (__darwin_time_t)timeStamp;
pathcpy(erpath, caches->root);
pathcat(erpath, caches->erpropcache->rpath);
(void)sunlink(caches->cachefd, erpath);
if (timeStamp != 0LL) {
if ((errnum = _sutimes(caches->cachefd, erpath, O_CREAT, times))) {
rval = errnum; goto finish;
}
}
rval = 0;
finish:
if (rval)
LOGERRxlate(erpath, NULL, rval);
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) {
result = ENOMEM;
goto finish;
}
CFArrayAppendValue(locsList, ENGLISHKEY);
if (!CFArrayContainsValue(locsList, range, ENGLISHKEY)) {
result = ENOMEM; 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 errnum, result = ELAST + 1;
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 ((errnum = get_locres_info(caches, locRsrcDir, prefPath, &prefsb,
locCacheDir, &validModTime))) {
result = errnum; goto finish;
}
if (sdeepunlink(caches->cachefd, locCacheDir) == -1 && errno == EROFS) {
result = errno; goto finish;
}
if ((errnum = sdeepmkdir(caches->cachefd,locCacheDir,kCacheDirMode))) {
result = errnum; goto finish;
}
errnum = _writeEFILoginResources(caches, prefPath, &prefsb, locCacheDir);
if (errnum) {
OSKextLog(NULL, kOSKextLogErrorLevel|kOSKextLogFileAccessFlag,
"_writeEFILoginResources() failed: %d",
(result == -1) ? errno : result);
(void)sdeepunlink(caches->cachefd, locCacheDir);
result = errnum; goto finish;
}
if ((errnum = stat(locCacheDir, &cachesb))) {
OSKextLog(NULL, kOSKextLogWarningLevel|kOSKextLogFileAccessFlag,
"%s: %s", locCacheDir, strerror(errno));
result = errnum; goto finish;
}
cachesb.st_mtime = validModTime;
TIMESPEC_TO_TIMEVAL(×[0], &cachesb.st_atimespec);
TIMESPEC_TO_TIMEVAL(×[1], &cachesb.st_mtimespec);
if ((errnum = _sutimes(caches->cachefd, locCacheDir, O_RDONLY, times))) {
result = errnum; goto finish;
}
result = 0;
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 stack_bsdname[DEVMAXPATHSIZE];
char * lookup_bsdname = caches->bsdname;
CFArrayRef dataPartitions = NULL; size_t fullLen;
char fulldev[DEVMAXPATHSIZE];
#if DEBUG_REGISTRY
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) {
if (!CFStringGetFileSystemRepresentation(dpBsdName, stack_bsdname,
sizeof(stack_bsdname)))
goto finish;
lookup_bsdname = stack_bsdname;
}
}
fullLen = snprintf(fulldev, sizeof(fulldev), "/dev/%s", lookup_bsdname);
if (fullLen >= sizeof(fulldev)) {
goto finish;
}
#if DEBUG_REGISTRY
if (BLGetParentDeviceAndPartitionType(NULL ,
fulldev, parentdevname, &partitionNum, &partitionType))
{
goto finish;
}
if (partitionType == kBLPartitionType_APM) {
apm = true;
}
#endif
if (apm) {
CFDictionaryRef pbDict, mk2Dict, kcDict;
pbDict = CFDictionaryGetValue(caches->cacheinfo, kBCPostBootKey);
if (!pbDict || CFGetTypeID(pbDict) != CFDictionaryGetTypeID()) goto finish;
kcDict = CFDictionaryGetValue(pbDict, kBCKernelcacheV1Key);
if (!kcDict)
kcDict = CFDictionaryGetValue(pbDict, kBCKernelcacheV2Key);
mk2Dict = CFDictionaryGetValue(pbDict, kBCMKext2Key);
if (!kcDict && !mk2Dict && caches->ofbooter.rpath[0] == '\0')
goto finish;
}
rval = (CFArrayGetCount(bparts) > 0);
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);
return rval;
}
CFArrayRef
BRCopyActiveBootPartitions(CFURLRef volRoot)
{
CFArrayRef bparts, 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;
bparts = CFDictionaryGetValue(binfo, kBLAuxiliaryPartitionsKey);
if (bparts && CFArrayGetCount(bparts)) {
rval = CFRetain(bparts);
}
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))) {
result = ENOMEM; goto finish;
}
DASessionScheduleWithRunLoop(session, CFRunLoopGetCurrent(), toggleMode);
if (!(volURL=CFURLCreateFromFileSystemRepresentation(nil, (void*)mount,
strlen(mount), true))) {
result = ENOMEM; goto finish;
}
if (!(disk = DADiskCreateFromVolumePath(nil, session, volURL))) {
result = ENOMEM; goto finish;
}
DADiskMountWithArguments(disk, NULL, kDADiskMountOptionDefault, _daDone,
&dis, mountargs);
while (dis == (void*)kCFNull) {
CFRunLoopRunInMode(toggleMode, 0, true); }
if (dis) {
result = DADissenterGetStatus(dis); if (result == 0) result = ELAST + 1;
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->f_flags to %#x: error %#x", mount,
mntgoal, result);
}
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;
}
struct nameAndUUID {
uint32_t nbytes;
struct attrreference nameref;
uuid_t uuid;
char namedata[NAME_MAX+1];
};
int
copyVolumeInfo(const char *vol_path, uuid_t *vol_uuid, CFStringRef *cslvf_uuid,
char **vol_bsd, char **vol_name)
{
int bsderr, rval = ENODEV;
struct nameAndUUID attrs;
struct attrlist attrdesc = { ATTR_BIT_MAP_COUNT, 0, 0, ATTR_VOL_INFO |
ATTR_VOL_NAME | ATTR_VOL_UUID, 0, 0, 0 };
struct statfs sfs;
char *bsdname;
io_object_t ioObj = IO_OBJECT_NULL;
CFTypeRef regEntry = NULL;
if ((bsderr=getattrlist(vol_path, &attrdesc, &attrs, sizeof(attrs), 0))
|| attrs.nbytes >= sizeof(attrs)) {
rval = errno; goto finish;
}
if (vol_bsd || cslvf_uuid) {
if ((bsderr = statfs(vol_path, &sfs))) {
rval = errno; goto finish;
}
bsdname = sfs.f_mntfromname;
if (strncmp(bsdname, _PATH_DEV, strlen(_PATH_DEV)) == 0) {
bsdname += strlen(_PATH_DEV);
}
}
if (vol_uuid) {
memcpy(*vol_uuid, attrs.uuid, sizeof(uuid_t));
}
if (cslvf_uuid) {
CFDictionaryRef matching; matching = IOBSDNameMatching(kIOMasterPortDefault, 0, bsdname);
if (!matching) {
rval = ENOMEM; goto finish;
}
ioObj = IOServiceGetMatchingService(kIOMasterPortDefault, matching);
matching = NULL; if (ioObj == IO_OBJECT_NULL) {
rval = ENODEV; goto finish;
}
regEntry = IORegistryEntryCreateCFProperty(ioObj,
CFSTR(kCoreStorageLVFUUIDKey), nil, 0);
if (regEntry && CFGetTypeID(regEntry) == CFStringGetTypeID()) {
*cslvf_uuid = (CFStringRef)CFRetain(regEntry);
} else {
*cslvf_uuid = NULL;
}
}
if (vol_bsd) {
if (strlcpy(*vol_bsd, bsdname, DEVMAXPATHSIZE) >= DEVMAXPATHSIZE) {
rval = EOVERFLOW; goto finish;
}
}
if (vol_name) {
char *volname = (char*)&attrs.nameref + attrs.nameref.attr_dataoffset;
(void)strlcpy(*vol_name, volname, NAME_MAX);
}
rval = 0;
finish:
if (rval) {
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s: %s", vol_path, strerror(rval));
}
if (regEntry) CFRelease(regEntry);
if (ioObj != IO_OBJECT_NULL) IOObjectRelease(ioObj);
return rval;
}