#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 <IOKit/kext/OSKext.h>
#include <IOKit/kext/OSKextPrivate.h>
#include "bootcaches.h" // includes CF
#include "bootroot_internal.h" // kBRUpdateOpts_t
#include "fork_program.h"
#include "kext_tools_util.h"
#include "safecalls.h"
#define kBRDiskArbMaxRetries (10)
static void removeTrailingSlashes(char * path);
#if DEV_KERNEL_SUPPORT
static int getExtraKernelCachePaths(struct bootCaches *caches);
#endif
#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, errval) do { \
char *c2cpy = ctx2, ctx[256]; \
if (ctx2 != NULL) { \
snprintf(ctx, sizeof(ctx), "%s: %s", ctx1, c2cpy); \
} else { \
snprintf(ctx, sizeof(ctx), "%s", ctx1); \
} \
\
if (errval == -1) errval = errno; \
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, \
"%s: %s", ctx, strerror(errval)); \
} while(0)
void
destroyCaches(struct bootCaches *caches)
{
if (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->exts) free(caches->exts);
if (caches->csfde_uuid) CFRelease(caches->csfde_uuid);
#if DEV_KERNEL_SUPPORT
if (caches->extraKernelCachePaths) free(caches->extraKernelCachePaths);
#endif
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); caches->exts = NULL;
caches->nexts = 0;
#if DEV_KERNEL_SUPPORT
caches->kernelsCount = 0;
#endif
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;
}
str = CFDictionaryGetValue(erDict, kBCCSFDEBackgroundImageKey);
if (str && CFGetTypeID(str) == CFStringGetTypeID() &&
CFStringGetFileSystemRepresentation(str, caches->bgImage,
sizeof(caches->bgImage))) {
keyCount--;
}
}
keyCount--; }
kcacheKeys = 0;
if (CFDictionaryContainsKey(dict, kBCMKextKey)) kcacheKeys++;
if (CFDictionaryContainsKey(dict, kBCMKext2Key)) kcacheKeys++;
if (CFDictionaryContainsKey(dict, kBCKernelcacheV1Key)) kcacheKeys++;
if (CFDictionaryContainsKey(dict, kBCKernelcacheV2Key)) kcacheKeys++;
if (CFDictionaryContainsKey(dict, kBCKernelcacheV3Key)) kcacheKeys++;
if (kcacheKeys > 1) {
goto finish;
}
do {
mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCKernelcacheV1Key);
if (!mkDict) {
mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCKernelcacheV2Key);
}
if (!mkDict) {
mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCKernelcacheV3Key);
}
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)
size_t bufsize = 0;
apaths = (CFArrayRef)CFDictionaryGetValue(mkDict, kBCExtensionsDirKey);
if (apaths && CFArrayGetTypeID() == CFGetTypeID(apaths)) {
int i;
char *bufptr;
char tempbuf[PATH_MAX];
caches->nexts = (int) CFArrayGetCount(apaths);
if (caches->nexts == 0) goto finish;
caches->exts = malloc(caches->nexts * PATH_MAX);
if (caches->exts == NULL) {
OSKextLogMemError();
goto finish;
}
bufptr = caches->exts;
for (i = 0; i < caches->nexts; i++) {
str = CFArrayGetValueAtIndex(apaths, i);
if (!str || CFGetTypeID(str) != CFStringGetTypeID()) {
goto finish;
}
if (!CFStringGetFileSystemRepresentation(str, tempbuf,
sizeof(tempbuf))) {
goto finish;
}
pathcpy(bufptr, tempbuf);
bufsize += (strlen(tempbuf) + 1);
bufptr += (strlen(tempbuf) + 1);
}
}
else {
caches->exts = malloc(PATH_MAX);
if (caches->exts == NULL) {
OSKextLogMemError();
goto finish;
}
caches->nexts = 1;
str = (CFStringRef)CFDictionaryGetValue(mkDict, kBCExtensionsDirKey);
if (!str || CFGetTypeID(str) != CFStringGetTypeID()) {
goto finish;
}
if (!CFStringGetFileSystemRepresentation(str, caches->exts,
PATH_MAX)) {
goto finish;
}
bufsize = (strlen(caches->exts) + 1);
}
if (bufsize) {
caches->exts = reallocf(caches->exts, bufsize);
if (caches->exts == NULL) {
OSKextLogMemError();
goto finish;
}
}
if (isKernelcache) {
str = (CFStringRef)CFDictionaryGetValue(mkDict,
kBCKernelPathKey);
if (!str || CFGetTypeID(str) != CFStringGetTypeID()) {
goto finish;
}
if (!CFStringGetFileSystemRepresentation(str, caches->kernelpath,
sizeof(caches->kernelpath))) {
goto finish;
}
#if DEV_KERNEL_SUPPORT
getExtraKernelCachePaths(caches);
#endif
}
keyCount--; }
keyCount--; }
if (keyCount == 0 && (unsigned)rpsindex == caches->nrps) {
rval = 0;
caches->cacheinfo = CFRetain(bcDict); }
finish:
if (createdStr) CFRelease(createdStr);
if (rval != 0 && caches->exts != NULL) {
free(caches->exts);
caches->exts = NULL;
caches->nexts = 0;
}
return rval;
}
#if DEV_KERNEL_SUPPORT
static int
getExtraKernelCachePaths(struct bootCaches *caches)
{
int result = -1;
int maxCacheCount = 0;
int cacheIndex = 0;
CFURLRef kernURL = NULL; CFURLRef kernParentURL = NULL; CFURLEnumeratorRef myEnumerator = NULL; CFURLRef enumURL = NULL; CFStringRef tmpCFString = NULL; CFArrayRef resultArray = NULL; char * tmpKernelPath = NULL; char * tmpCachePath = NULL; char * suffixPtr = NULL;
caches->kernelsCount = 0;
caches->nekcp = 0;
tmpKernelPath = malloc(PATH_MAX);
tmpCachePath = malloc(PATH_MAX);
if (tmpKernelPath == NULL || tmpCachePath == NULL) goto finish;
if (strlcpy(tmpKernelPath, caches->root, PATH_MAX) >= PATH_MAX)
goto finish;
if (strlcat(tmpKernelPath, caches->kernelpath, PATH_MAX) >= PATH_MAX)
goto finish;
kernURL = CFURLCreateFromFileSystemRepresentation(
NULL,
(const UInt8 *)tmpKernelPath,
strlen(tmpKernelPath),
true );
if (kernURL == NULL) {
goto finish;
}
kernParentURL = CFURLCreateCopyDeletingLastPathComponent(NULL,
kernURL );
if (kernParentURL == NULL) {
goto finish;
}
tmpCFString = CFURLCopyLastPathComponent(kernParentURL);
if (tmpCFString == NULL ||
CFStringCompare(tmpCFString, CFSTR("Kernels"), 0) != kCFCompareEqualTo) {
goto finish;
}
myEnumerator = CFURLEnumeratorCreateForDirectoryURL(
NULL,
kernParentURL,
kCFURLEnumeratorDefaultBehavior,
NULL );
if (myEnumerator == NULL) {
goto finish;
}
while (CFURLEnumeratorGetNextURL(myEnumerator,
&enumURL,
NULL) == kCFURLEnumeratorSuccess) {
SAFE_RELEASE_NULL(tmpCFString);
SAFE_FREE_NULL(suffixPtr);
tmpCFString = CFURLCopyLastPathComponent(enumURL);
if (tmpCFString == NULL) continue;
if (kCFCompareEqualTo == CFStringCompare(tmpCFString,
CFSTR("kernel"),
kCFCompareAnchored)) {
caches->kernelsCount++;
continue;
}
if (CFStringHasPrefix(tmpCFString,
CFSTR("kernel.")) == false) {
continue;
}
SAFE_RELEASE_NULL(resultArray);
resultArray = CFStringCreateArrayWithFindResults(
NULL,
tmpCFString,
CFSTR("."),
CFRangeMake(0, CFStringGetLength(tmpCFString)),
0);
if (resultArray && CFArrayGetCount(resultArray) > 1) {
continue;
}
caches->kernelsCount++;
SAFE_RELEASE(tmpCFString);
tmpCFString = CFURLCopyPathExtension(enumURL);
if (tmpCFString == NULL) continue;
suffixPtr = createUTF8CStringForCFString(tmpCFString);
if (suffixPtr == NULL) continue;
if (strlcpy(tmpCachePath, caches->kext_boot_cache_file->rpath, PATH_MAX) >= PATH_MAX)
continue;
if (strlcat(tmpCachePath, ".", PATH_MAX) >= PATH_MAX)
continue;
if (strlcat(tmpCachePath, suffixPtr, PATH_MAX) >= PATH_MAX)
continue;
SAFE_RELEASE_NULL(tmpCFString);
tmpCFString = CFStringCreateWithCString(kCFAllocatorDefault,
tmpCachePath,
kCFStringEncodingUTF8);
if (tmpCFString == NULL) continue;
if (cacheIndex >= maxCacheCount) {
cachedPath * tempCPPtr;
maxCacheCount += 2;
tempCPPtr = (cachedPath *) calloc(maxCacheCount, sizeof(cachedPath));
if (tempCPPtr == NULL) goto finish;
if (caches->extraKernelCachePaths) {
bcopy(caches->extraKernelCachePaths,
tempCPPtr,
sizeof(*caches->extraKernelCachePaths) * caches->nekcp);
SAFE_FREE(caches->extraKernelCachePaths);
}
caches->extraKernelCachePaths = tempCPPtr;
}
MAKE_CACHEDPATH(&caches->extraKernelCachePaths[cacheIndex],
caches,
tmpCFString);
cacheIndex++;
caches->nekcp++;
}
result = 0;
finish:
if (result != 0) {
SAFE_FREE_NULL(caches->extraKernelCachePaths);
caches->nekcp = 0;
}
SAFE_RELEASE(kernURL);
SAFE_RELEASE(kernParentURL);
SAFE_RELEASE(myEnumerator);
SAFE_RELEASE(tmpCFString);
SAFE_RELEASE(resultArray);
SAFE_FREE(tmpKernelPath);
SAFE_FREE(tmpCachePath);
SAFE_FREE(suffixPtr);
#if 0
OSKextLogCFString(NULL,
kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
CFSTR("%s: kernelsCount %d nekcp %d"),
__func__,
caches->kernelsCount,
caches->nekcp);
if (result == 0) {
int j;
for (j = 0; j < cacheIndex; j++) {
OSKextLogCFString(NULL,
kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
CFSTR("%s: extraKernelCachePaths at %d \"%s\""),
__func__,
j,
caches->extraKernelCachePaths[j].rpath);
}
}
#endif
return result;
}
#endif
static int
ensureCacheDirs(struct bootCaches *caches)
{
int errnum, result = ELAST + 1;
struct statfs sfs;
char *errname;
struct stat sb;
char cachedir[PATH_MAX], uuiddir[PATH_MAX]; Boolean checkOnly = false;
errname = caches->root;
if (statfs(caches->root, &sfs) == 0) {
if (sfs.f_flags & MNT_IGNORE_OWNERSHIP) {
checkOnly = true;
}
} 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 (checkOnly) {
result = ENOTSUP; goto finish;
}
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) {
if (checkOnly) {
result = ENOTSUP; goto finish;
}
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)
CFPropertyListCreateWithData(nil,
data,
kCFPropertyListImmutable,
NULL,
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;
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";
if ((errnum = copyVolumeInfo(volRoot, &vol_uuid, &caches->csfde_uuid,
caches->bsdname, caches->defLabel))){
errno = errnum; goto finish;
}
if ((opts & kBRAnyBootStamps) == 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 < kBRDiskArbMaxRetries);
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, kBROptsNone);
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 kBRTaintFile ".notBootRootDefault"
#define MAKETAINTPATH(taintpath, mount, subdir) do { \
unsigned fullLen; \
fullLen = snprintf(taintpath, sizeof(taintpath), "%s/%s/%s", \
mount, subdir ? subdir : "", kBRTaintFile); \
if (fullLen >= sizeof(taintpath)) goto finish; \
} while(0)
Boolean
notBRDefault(const char *mount, const char *subdir)
{
Boolean ismarked = false;
char taintf[PATH_MAX];
struct stat sb;
MAKETAINTPATH(taintf, mount, subdir);
ismarked = stat(taintf, &sb) == 0;
finish:
return ismarked;
}
#define OODMSG "not cached."
Boolean
needUpdates(struct bootCaches *caches, BRUpdateOpts_t opts,
Boolean *rps, Boolean *booters, Boolean *misc,
OSKextLogSpec oodLogSpec)
{
Boolean rpsOOD, bootersOOD, miscOOD, anyOOD;
cachedPath *cp;
rpsOOD = bootersOOD = miscOOD = anyOOD = false;
if ((opts & kBRUCachesAnyRoot) == false &&
notBRDefault(caches->root,kTSCacheDir)){
rpsOOD = bootersOOD = miscOOD = anyOOD = true;
}
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 DEV_KERNEL_SUPPORT
if (caches->extraKernelCachePaths) {
for (cp = caches->extraKernelCachePaths;
cp < &caches->extraKernelCachePaths[caches->nekcp];
cp++) {
if (needsUpdate(caches->root, cp)) {
OSKextLog(NULL, oodLogSpec, "%s " OODMSG, cp->rpath);
anyOOD = rpsOOD = true;
}
}
}
#endif
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 = ensureCacheDirs(caches))) {
return anyErr;
}
for (cp = caches->rpspaths; cp < &caches->rpspaths[caches->nrps]; cp++) {
anyErr |= updateStamp(caches->root, cp, caches->cachefd, command);
}
#if DEV_KERNEL_SUPPORT
if (caches->extraKernelCachePaths) {
for (cp = caches->extraKernelCachePaths;
cp < &caches->extraKernelCachePaths[caches->nekcp];
cp++) {
anyErr |= updateStamp(caches->root, cp, caches->cachefd, command);
}
}
#endif
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);
}
anyErr |= markNotBRDefault(caches->cachefd,caches->root,kTSCacheDir,false);
return anyErr;
}
int
markNotBRDefault(int scopefd, const char *mount, const char* subdir,
Boolean marking)
{
int rval = ELAST + 1;
char taintf[PATH_MAX];
if (!mount) {
rval = EINVAL; goto finish;
}
MAKETAINTPATH(taintf, mount, subdir);
(void)sunlink(scopefd, taintf);
if (marking) {
int fd = sopen(scopefd, taintf, O_CREAT, kCacheFileMode);
if (fd == -1) {
rval = errno;
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Creating %s: %s", taintf, strerror(rval));
goto finish;
}
close(fd);
}
rval = 0;
finish:
return rval;
}
int
taintDefaultStamps(CFStringRef targetBSD)
{
int rval = ELAST + 1;
struct bootCaches *defRootCaches = NULL;
char *errmsg = NULL;
int nfsys, i;
size_t bufsz;
struct statfs *mounts = NULL;
errmsg = "Error getting mount list.";
if (-1 == (nfsys = getfsstat(NULL, 0, MNT_NOWAIT))) {
rval = errno; goto finish;
}
bufsz = nfsys * sizeof(struct statfs);
if (!(mounts = malloc(bufsz))) {
rval = errno; goto finish;
}
if (-1 == getfsstat(mounts, (int)bufsz, MNT_NOWAIT)) {
rval = errno; goto finish;
}
errmsg = "error examining helper partitions";
for (i = 0; i < nfsys; i++) {
struct statfs *sfs = &mounts[i];
CFArrayRef helpers;
CFStringRef helper;
if (sfs->f_flags & MNT_LOCAL && strcmp(sfs->f_fstypename, "devfs")) {
CFURLRef volURL;
volURL = CFURLCreateFromFileSystemRepresentation(nil,
(UInt8*)sfs->f_mntonname,
strlen(sfs->f_mntonname),
1 );
if (!volURL) {
rval = ENOMEM; goto finish;
}
if ((helpers = BRCopyActiveBootPartitions(volURL))) {
CFIndex hidx = CFArrayGetCount(helpers);
while (hidx--) {
helper = CFArrayGetValueAtIndex(helpers, hidx);
if (helper && CFEqual(helper, targetBSD)) {
defRootCaches = readBootCaches(sfs->f_mntonname, 0);
}
}
CFRelease(helpers);
}
CFRelease(volURL);
}
if (defRootCaches) break;
}
errmsg = NULL;
if (defRootCaches) {
rval = markNotBRDefault(defRootCaches->cachefd, defRootCaches->root,
kTSCacheDir, true); destroyCaches(defRootCaches);
} else {
rval = 0;
}
finish:
if (errmsg) {
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s", errmsg);
}
if (mounts) free(mounts);
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 = NULL;
char fullkernelp[PATH_MAX] = "";
Boolean generateKernelcache = false;
int mkextVersion = 0;
if (!caches->kext_boot_cache_file
) {
goto finish;
}
fullextsp = malloc(caches->nexts * PATH_MAX);
if (!fullextsp) goto finish;
*fullextsp = 0x00;
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) {
mkDict = CFDictionaryGetValue(pbDict, kBCKernelcacheV3Key);
}
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 + caches->nexts + 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;
char *extsDirPtr = caches->exts;
char *tempExtsDirPtr = fullextsp;
for (i = 0; i < caches->nexts; i++) {
pathcpy(tempExtsDirPtr, caches->root);
removeTrailingSlashes(tempExtsDirPtr);
pathcat(tempExtsDirPtr, extsDirPtr);
kcargs[argi++] = tempExtsDirPtr;
extsDirPtr += (strlen(extsDirPtr) + 1);
tempExtsDirPtr += (strlen(tempExtsDirPtr) + 1);
}
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 (fullextsp) free(fullextsp);
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 fullPath[PATH_MAX] = "";
struct stat statbuffer;
time_t validModtime = 0;
time_t kernelcacheModtime = 0;
if (cache_path == NULL)
goto finish;
char *bufptr;
int i;
bufptr = caches->exts;
for (i = 0; i < caches->nexts; i++) {
pathcpy(fullPath, caches->root);
removeTrailingSlashes(fullPath);
pathcat(fullPath, bufptr);
if (stat(fullPath, &statbuffer) == 0) {
#if 0
OSKextLogCFString(NULL,
kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
CFSTR("%s - %ld <- mod time of %s "),
__func__, statbuffer.st_mtime, fullPath);
#endif
if (statbuffer.st_mtime + 1 > validModtime) {
validModtime = statbuffer.st_mtime + 1;
}
}
else {
OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag,
"Warning: %s: %s", fullPath, strerror(errno));
}
bufptr += (strlen(bufptr) + 1);
fullPath[0] = 0x00;
}
if (kernel_path[0]) {
pathcpy(fullPath, caches->root);
removeTrailingSlashes(fullPath);
pathcat(fullPath, kernel_path);
if (stat(fullPath, &statbuffer) == -1) {
OSKextLog( NULL,
kOSKextLogBasicLevel | kOSKextLogFileAccessFlag,
"Note: %s: %s", fullPath, strerror(errno));
goto finish;
}
#if 0
OSKextLogCFString(NULL,
kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
CFSTR("%s - %ld <- mod time of %s "),
__func__, statbuffer.st_mtime, fullPath);
#endif
if (statbuffer.st_mtime > validModtime) {
validModtime = statbuffer.st_mtime + 1;
}
}
needsrebuild = true; pathcpy(fullPath, caches->root);
removeTrailingSlashes(fullPath);
pathcat(fullPath, cache_path);
if (stat(fullPath, &statbuffer) == -1) {
goto finish;
}
#if 0
OSKextLogCFString(NULL,
kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
CFSTR("%s - %ld <- mod time of %s "),
__func__, statbuffer.st_mtime, fullPath);
#endif
kernelcacheModtime = statbuffer.st_mtime;
needsrebuild = (kernelcacheModtime != 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"
#define kFDECacheFile kCSFDEPropertyCacheDir"/"kCSFDEPropertyCacheFileEncrypted
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);
if (!(relpath = strstr(parentpath, kFDECacheFile))) {
rval = EINVAL; LOGERRxlate(dstpath, "missing" kFDECacheFile, rval);
goto finish;
}
relpath[0] = '\0';
pathcpy(cookiepath, parentpath);
pathcat(cookiepath, kOrigInitCookieDir);
if ((bsderr = sdeepmkdir(scopefd, cookiepath, kCacheDirMode))) {
rval = bsderr; LOGERRxlate(cookiepath, NULL, rval); goto finish;
}
pathcat(cookiepath, kOrigInitCookieFile);
if (0 != stat(cookiepath, &sb)) {
if ((fd = sopen(scopefd, cookiepath, O_CREAT, kCacheFileMode)) < 0) {
rval = errno; LOGERRxlate(cookiepath, NULL, rval); 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; LOGERRxlate("CSFDEInitPropertyCache", parentpath, rval);
goto finish;
}
if (-1 == stat(dstpath, &sb)) {
rval = errno; LOGERRxlate(dstpath, NULL, rval); 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;
LOGERRxlate("no CoreStorageCopyPVWipeKeyUUID()", NULL, rval);
goto finish;
}
wipeKeyUUID = CoreStorageCopyPVWipeKeyUUID(cspvbsd);
if (!wipeKeyUUID) {
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"CoreStorageCopyPVWipeKeyUUID(%s) failed", cspvbsd);
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;
}
if ((errnum = sdeepmkdir(scopefd, dstparent, kCacheDirMode))) {
rval = errnum; LOGERRxlate(dstparent, NULL, rval); goto finish;
}
if (CSFDEWritePropertyCacheToFD!=NULL) {
erfd = sopen(scopefd, dstpath, O_CREAT|O_RDWR, kCacheFileMode);
if (-1 == erfd) {
rval = errno; LOGERRxlate(dstpath, NULL, rval); goto finish;
}
if (!CSFDEWritePropertyCacheToFD(ectx, erfd, wipeKeyUUID)) {
rval = ELAST + 1; LOGERRxlate("CSFDEWritePropertyCacheToFD", dstpath, rval);
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);
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;
char erpath[PATH_MAX];
int erfd = -1;
errmsg = "invalid argument";
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;
}
errmsg = NULL; if ((errnum = writeCSFDEProps(caches->cachefd, ectx, bsdname, erpath))) {
rval = errnum; goto finish;
}
rval = 0;
finish:
if (erfd != -1) close (erfd);
if (dataVolumes) CFRelease(dataVolumes);
if (ectx) CFRelease(ectx);
if (rval && errmsg) {
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 = ensureCacheDirs(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;
char bgImagePath[PATH_MAX];
if (!validModTime) {
rval = EINVAL; LOGERRxlate("get_locres_info", NULL, rval); goto finish;
}
pathcpy(locRsrcDir, caches->root);
pathcat(locRsrcDir, caches->locSource);
if (stat(locRsrcDir, &sb)) {
rval = errno; LOGERRxlate(locRsrcDir, NULL, rval); 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) {
rval = errno; LOGERRxlate(prefPath, NULL, rval); goto finish;
}
}
if (caches->bgImage[0]) {
pathcpy(bgImagePath, caches->root);
pathcat(bgImagePath, caches->bgImage);
if (stat(bgImagePath, &sb) == 0 &&
sb.st_mtime > newestTime) {
newestTime = sb.st_mtime;
}
}
*validModTime = newestTime + 1;
pathcpy(locCacheDir, caches->root);
pathcat(locCacheDir, caches->efiloccache->rpath);
rval = 0;
finish:
return rval;
}
Boolean
check_loccache(struct bootCaches *caches)
{
Boolean needsupdate = false; struct stat prefsb, cachesb;
char erpath[PATH_MAX];
char locRsrcDir[PATH_MAX], prefPath[PATH_MAX];
char locCacheDir[PATH_MAX];
time_t validModTime = 0;
if (!caches->efiloccache) goto finish;
pathcpy(erpath, caches->root);
pathcat(erpath, caches->erpropcache->rpath);
if (stat(erpath, &cachesb) == -1 && errno == ENOENT) {
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 = 0;
int fd = -1;
struct timeval times[2];
bzero(&prefsb, sizeof(prefsb)); 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; LOGERRxlate(locCacheDir, NULL, result); goto finish;
}
if ((errnum = sdeepmkdir(caches->cachefd,locCacheDir,kCacheDirMode))) {
result = errnum; LOGERRxlate(locCacheDir, NULL, result); goto finish;
}
errnum = _writeEFILoginResources(caches, prefPath, &prefsb, locCacheDir);
if (errnum) {
(void)sdeepunlink(caches->cachefd, locCacheDir);
result = errnum;
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Warning: _writeEFILoginResources failed");
goto finish;
}
if ((errnum = stat(locCacheDir, &cachesb))) {
result = errnum; LOGERRxlate(locCacheDir, NULL, result); 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; LOGERRxlate(locCacheDir, NULL, result); 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 (path[scanIndex] == '/') {
path[scanIndex] = '\0';
if (scanIndex == 0) break;
scanIndex--;
}
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[DEVMAXPATHSIZE], char vol_name[NAME_MAX])
{
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;
}