#include <bless.h>
#include <miscfs/devfs/devfs.h> // UID_ROOT, GID_WHEEL
#include <fcntl.h>
#include <libgen.h>
#include <mach/mach_error.h>
#include <mach/mach_port.h> // mach_port_allocate()
#include <servers/bootstrap.h>
#include <sysexits.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/xattr.h>
#include <unistd.h>
#include <IOKit/kext/kextmanager_types.h>
#include <IOKit/kext/OSKextPrivate.h>
#include <IOKit/kext/kextmanager_types.h>
#include <IOKit/kext/kextmanager_mig.h>
#include <bootfiles.h>
#include <CoreFoundation/CoreFoundation.h>
#include <DiskArbitration/DiskArbitration.h>
#include <DiskArbitration/DiskArbitrationPrivate.h>
#include "bootcaches.h"
#include "bootroot_internal.h"
#include "fork_program.h"
#include "safecalls.h"
#include "kext_tools_util.h"
static mach_port_t sBRUptLock = MACH_PORT_NULL;
static uuid_t s_vol_uuid;
static mach_port_t sKextdPort = MACH_PORT_NULL;
enum bootReversions {
nothingSerious = 0,
noLabel, copyingOFBooter, copyingEFIBooter, copiedBooters, activatingOFBooter, activatingEFIBooter, activatedBooters, };
enum blessIndices {
kSystemFolderIdx = 0,
kEFIBooterIdx = 1
};
const char * bootReversionsStrings[] = {
NULL, "Label deleted",
"Unlinking and copying BootX booter",
"Unlinking and copying EFI booter",
"Booters copied",
"Activating BootX",
"Activating EFI booter",
"Booters activated"
};
struct updatingVol {
struct bootCaches *caches; struct bootCaches *hostCaches; char srcRoot[PATH_MAX]; char altERpropcache[PATH_MAX]; char hostroot[PATH_MAX]; int hostfd; CFDictionaryRef bootPrefOverrides; uuid_string_t root_uuid;
Boolean expectUpToDate; Boolean earlyBoot; Boolean doRPS, doMisc, doBooters; Boolean doSanitize; enum bootReversions changestate; CFArrayRef boots; DASessionRef dasession; OSKextLogSpec warnLogSpec; OSKextLogSpec errLogSpec;
int bootIdx; char bsdname[DEVMAXPATHSIZE]; DADiskRef curBoot; char curMount[MNAMELEN]; int curbootfd; char curRPS[PATH_MAX]; char efidst[PATH_MAX], ofdst[PATH_MAX];
Boolean onAPM;
};
#define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;}
#define OLDEXT ".old"
#define NEWEXT ".new"
#define CONTENTEXT ".contentDetails"
#define BOOTPLIST_NAME "com.apple.Boot.plist"
#define BOOTPLIST_APM_NAME "com.apple.boot.plist"
static int mountBoot(struct updatingVol *up);
static void unmountBoot(struct updatingVol *up);
static int ucopyRPS(struct updatingVol *s); static int ucopyMisc(struct updatingVol *s); static int ucopyBooters(struct updatingVol *s); static int moveLabels(struct updatingVol *s); static int nukeBRLabels(struct updatingVol *s); static int activateBooters(struct updatingVol *s); static int activateRPS(struct updatingVol *s); static int activateMisc(struct updatingVol *s); static int nukeFallbacks(struct updatingVol *s);
static int eraseRPS(struct updatingVol *up, char *toErase);
static int revertState(struct updatingVol *up);
#define pathcpy(dst, src) do { \
Boolean useErrno = (errno == 0); \
if (useErrno) errno = ENAMETOOLONG; \
if (strlcpy(dst, src, PATH_MAX) >= PATH_MAX) goto finish; \
if (useErrno) errno = 0; \
} while(0)
#define pathcat(dst, src) do { \
Boolean useErrno = (errno == 0); \
if (useErrno) errno = ENAMETOOLONG; \
if (strlcat(dst, src, PATH_MAX) >= PATH_MAX) goto finish; \
if (useErrno) errno = 0; \
} while(0)
#define makebootpath(path, rpath) do { \
pathcpy(path, up->curMount); \
pathcat(path, rpath); \
} while(0)
static int getExitValueFor(errval)
{
int rval;
switch (errval) {
case ELAST + 1:
rval = EX_SOFTWARE;
break;
case EPERM:
rval = EX_NOPERM;
break;
case EAGAIN:
case ENOLCK:
rval = EX_OSERR;
break;
case -1:
switch (errno) {
case EIO:
rval = EX_IOERR;
break;
default:
rval = EX_OSERR;
break;
}
break;
default:
rval = errval;
}
return rval;
}
#define MOBILEBACKUPS_DIR "/.MobileBackups"
#define MDS_BULWARK "/.metadata_never_index"
#define MDS_DIR "/.Spotlight-V100"
#define FSEVENTS_BULWARK "/.fseventsd/no_log"
#define FSEVENTS_DIR "/.fseventsd"
static int
sanitizeBoot(struct updatingVol *up)
{
int rval = 0;
int fd = -1;
char bloatp[PATH_MAX], blockp[PATH_MAX];
Boolean blockMissing = true;
struct stat sb;
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag,
"Removing unnecessary bloat.");
if ((fstat(up->curbootfd, &sb) == 0) &&
(sb.st_uid != UID_ROOT || sb.st_gid != GID_WHEEL))
rval |= fchown(up->curbootfd, UID_ROOT, GID_WHEEL);
makebootpath(bloatp, MOBILEBACKUPS_DIR);
if (0 == (stat(bloatp, &sb))) {
fd = sdeepunlink(up->curbootfd, bloatp);
if (fd == -1) {
rval |= errno;
} else {
close(fd);
}
}
makebootpath(blockp, MDS_BULWARK);
if (-1 == stat(blockp, &sb) && errno == ENOENT) {
fd = sopen(up->curbootfd, blockp, O_CREAT, kCacheFileMode);
if (fd == -1) {
rval |= errno;
} else {
close(fd);
}
}
makebootpath(bloatp, MDS_DIR);
if (0 == (stat(bloatp, &sb))) {
rval |= sdeepunlink(up->curbootfd, bloatp);
}
makebootpath(bloatp, FSEVENTS_DIR);
makebootpath(blockp, FSEVENTS_BULWARK);
if (0 == (stat(bloatp, &sb))) {
if (-1 == stat(blockp, &sb) && errno == ENOENT) {
rval |= sdeepunlink(up->curbootfd, bloatp);
} else {
blockMissing = false;
}
}
if (blockMissing) {
rval |= sdeepmkdir(up->curbootfd, bloatp, kCacheDirMode);
fd = sopen(up->curbootfd, blockp, O_CREAT, kCacheFileMode);
if (fd == -1) {
rval |= errno;
} else {
close(fd);
}
}
finish:
if (rval)
OSKextLog(NULL,up->warnLogSpec,"Warning: trouble cleaning Apple_Boot");
return rval;
}
static void
checkForMissingFiles(struct updatingVol *up)
{
unsigned i;
char srcpath[PATH_MAX], dstpath[PATH_MAX];
struct stat sb;
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag,
"Looking for missing files.");
if (up->expectUpToDate) return;
if (!up->doMisc) {
for (i = 0; i < up->caches->nmisc; i++) {
pathcpy(srcpath, up->caches->root);
pathcat(srcpath, up->caches->miscpaths[i].rpath);
pathcpy(dstpath, up->curMount);
pathcat(dstpath, up->caches->miscpaths[i].rpath);
if (stat(srcpath, &sb) == 0) {
if (stat(dstpath, &sb) != 0 && errno == ENOENT) {
up->doMisc = true;
OSKextLog(nil,kOSKextLogFileAccessFlag|kOSKextLogBasicLevel,
"Helper partition missing misc files, forcing update");
break;
}
}
}
}
if (!up->doBooters) {
if (up->caches->efibooter.rpath[0]) {
pathcpy(dstpath, up->curMount);
pathcat(dstpath, up->caches->efibooter.rpath);
if (stat(dstpath, &sb) != 0 && errno == ENOENT) {
up->doBooters = true;
OSKextLog(NULL, kOSKextLogFileAccessFlag|kOSKextLogBasicLevel,
"Helper partition missing EFI booter, forcing update");
goto finish;
}
}
if (up->caches->ofbooter.rpath[0]) {
pathcpy(dstpath, up->curMount);
pathcat(dstpath, up->caches->ofbooter.rpath);
if (stat(dstpath, &sb) != 0 && errno == ENOENT) {
up->doBooters = true;
OSKextLog(NULL, kOSKextLogFileAccessFlag|kOSKextLogBasicLevel,
"Helper partition missing OF booter, forcing update");
goto finish;
}
}
}
finish:
return;
}
static int
updateBootHelpers(struct updatingVol *up, Boolean expectUpToDate)
{
int result = 0;
up->curbootfd = -1;
struct stat sb;
CFIndex bootcount, bootupdates = 0;
if ((result = fstat(up->caches->cachefd, &sb))) {
OSKextLog(NULL, up->errLogSpec, "fstat(cachefd): %s", strerror(errno));
goto finish;
}
bootcount = CFArrayGetCount(up->boots);
for (up->bootIdx = 0; up->bootIdx < bootcount; up->bootIdx++) {
up->changestate = nothingSerious; if ((result = mountBoot(up))) {
goto bootfail; }
if (up->doSanitize) {
(void)sanitizeBoot(up);
}
checkForMissingFiles(up);
if (up->doRPS && (result = ucopyRPS(up))) {
goto bootfail; }
if (up->doMisc) {
(void) ucopyMisc(up); }
if (expectUpToDate) {
if ((result = moveLabels(up))) {
goto bootfail;
}
} else {
if ((result = nukeBRLabels(up))) {
goto bootfail;
}
}
if (up->doBooters && (result = ucopyBooters(up))) {
goto bootfail; }
if (up->doBooters && (result = activateBooters(up))) { goto bootfail;
}
if (up->doRPS && (result = activateRPS(up))) { goto bootfail;
}
if ((result = activateMisc(up))) {
goto bootfail; }
up->changestate = nothingSerious;
bootupdates++; OSKextLog(NULL, kOSKextLogFileAccessFlag |
(expectUpToDate?kOSKextLogWarningLevel:kOSKextLogBasicLevel),
"Successfully updated helper partition %s.", up->bsdname);
bootfail:
if (up->changestate != nothingSerious) {
OSKextLog(NULL, up->errLogSpec,
"Error updating helper partition %s, state %d: %s.",
up->bsdname, up->changestate,
bootReversionsStrings[up->changestate]);
}
(void)revertState(up);
if (nukeFallbacks(up))
OSKextLog(NULL, up->errLogSpec,
"Helper partition %s may be untidy.", up->bsdname);
if (up->curBoot)
unmountBoot(up); }
if (bootupdates != bootcount) {
OSKextLog(NULL, up->errLogSpec, "failed to update helper partition%s",
bootcount - bootupdates == 1 ? "" : "s");
if (result == 0) {
result = ELAST + 1;
}
goto finish;
}
finish:
return result;
}
int
checkRebuildAllCaches(struct bootCaches *caches, int oodLogSpec)
{
int result; struct stat sb;
if ((result = fstat(caches->cachefd, &sb))) {
goto finish;
}
OSKextLog(NULL, kOSKextLogProgressLevel | kOSKextLogArchiveFlag,
"Ensuring %s's caches are up to date.", caches->root);
setenv("_com_apple_kextd_skiplocks", "1", 1);
if (check_kext_boot_cache_file(caches,
caches->kext_boot_cache_file->rpath, caches->kernel)) {
OSKextLog(nil, oodLogSpec, "rebuilding %s",
caches->kext_boot_cache_file->rpath);
if ((result = rebuild_kext_boot_cache_file(caches, true ,
caches->kext_boot_cache_file->rpath, caches->kernel))) {
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Error %d rebuilding %s.", result,
caches->kext_boot_cache_file->rpath);
goto finish;
}
} else {
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogArchiveFlag,
"Primary kext cache does not need update.");
}
if (check_csfde(caches)) {
OSKextLog(NULL,oodLogSpec,"rebuilding %s",caches->erpropcache->rpath);
if ((result = rebuild_csfde_cache(caches))) {
OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Error %d rebuilding %s.", result,
caches->erpropcache->rpath);
goto finish;
}
} else {
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogArchiveFlag,
"CSFDE property cache does not need update.");
}
if (check_loccache(caches)) {
OSKextLog(NULL,oodLogSpec,"rebuilding %s",caches->efiloccache->rpath);
if ((result = rebuild_loccache(caches))) {
OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogArchiveFlag,
"Warning: Error %d rebuilding %s",
result, caches->efiloccache->rpath);
}
}
result = 0;
finish:
return result;
}
#define BOOTCOUNT 1
static int
initContext(struct updatingVol *up, CFURLRef srcVol, CFStringRef helperBSDName,
int expectUpToDate)
{
int result;
const void *values[BOOTCOUNT] = { helperBSDName };
bzero(up, sizeof(struct updatingVol));
up->warnLogSpec = kOSKextLogArchiveFlag | kOSKextLogWarningLevel;
up->errLogSpec = kOSKextLogArchiveFlag | kOSKextLogErrorLevel;
up->expectUpToDate = expectUpToDate;
if (!CFURLGetFileSystemRepresentation(srcVol, true,
(UInt8 *)up->srcRoot,sizeof(up->srcRoot))){
OSKextLogStringError(NULL);
result = EX_OSERR;
goto finish;
}
if (expectUpToDate && getppid() == 2 ) {
up->earlyBoot = true;
} else {
if ((result = takeVolumeForPath(up->srcRoot))) { goto finish;
}
}
if (!(up->caches = readBootCachesForVolURL(srcVol))) {
if (errno == ENOENT) {
char cfURLBuf[PATH_MAX];
if (false == CFURLGetFileSystemRepresentation ( srcVol, true, (UInt8 *) cfURLBuf, PATH_MAX )) {
strlcpy(cfURLBuf, "(CFURL string unavailable)", PATH_MAX);
}
OSKextLog(NULL, up->warnLogSpec,
"%s: no " kBootCachesPath "; nothing to do", cfURLBuf);
}
if (errno) {
result = errno;
} else {
result = EINVAL;
}
goto finish;
}
if (!(up->dasession = DASessionCreate(nil))) {
result = ENOMEM;
goto finish;
}
DASessionScheduleWithRunLoop(up->dasession, CFRunLoopGetCurrent(),
kCFRunLoopDefaultMode);
if (helperBSDName) {
up->boots = CFArrayCreate(nil,values,BOOTCOUNT,&kCFTypeArrayCallBacks);
}
result = 0;
finish:
return result;
}
static void
cleanUpContext(struct updatingVol *up, int status)
{
if (up->curbootfd != -1)
close(up->curbootfd);
if (up->boots) CFRelease(up->boots);
if (up->dasession) {
DASessionUnscheduleFromRunLoop(up->dasession, CFRunLoopGetCurrent(),
kCFRunLoopDefaultMode);
CFRelease(up->dasession);
up->dasession = NULL;
}
if (up->hostCaches) destroyCaches(up->hostCaches);
if (up->caches) destroyCaches(up->caches);
putVolumeForPath(up->srcRoot, status);
}
#define BRDBG_OOD_HANG_BOOT_F "/var/db/.BRHangBootOnOODCaches"
#define BRDBG_HANG_MSG PRODUCT_NAME ": " BRDBG_OOD_HANG_BOOT_F \
"-> hanging on out of date caches"
#define BRDBG_CONS_MSG "[via /dev/console] " BRDBG_HANG_MSG "\n"
int
checkUpdateCachesAndBoots(
CFURLRef volumeURL,
Boolean force,
Boolean expectUpToDate,
Boolean cachesOnly)
{
int result = 0; OSKextLogSpec oodLogSpec = kOSKextLogGeneralFlag | kOSKextLogBasicLevel;
struct updatingVol up = { };
up.curbootfd = -1;
struct statfs sfs;
Boolean doAny;
if ((result = initContext(&up, volumeURL, NULL, expectUpToDate))) {
if (result == ENOENT || result == EINVAL) {
result = 0;
}
goto finish;
}
if (expectUpToDate) {
oodLogSpec = up.errLogSpec;
}
if ((result = checkRebuildAllCaches(up.caches, oodLogSpec))) {
goto finish;
}
if (cachesOnly) {
goto skipHelperUpdates;
}
if (!hasBootRootBoots(up.caches, &up.boots, NULL, &up.onAPM)) {
{
result = 0; OSKextLog(NULL, kOSKextLogBasicLevel | kOSKextLogFileAccessFlag,
"%s: no supported helper partitions to update.", up.srcRoot);
}
goto finish;
}
up.doSanitize = true;
doAny = needUpdates(up.caches, &up.doRPS, &up.doBooters, &up.doMisc,
oodLogSpec);
if (up.earlyBoot && doAny) {
struct stat sb;
int consfd = open(_PATH_CONSOLE, O_WRONLY|O_APPEND);
while (stat(BRDBG_OOD_HANG_BOOT_F, &sb) == 0) {
OSKextLog(NULL, up.errLogSpec, BRDBG_HANG_MSG);
if (consfd > -1)
write(consfd, BRDBG_CONS_MSG, sizeof(BRDBG_CONS_MSG)-1);
sleep(30);
}
}
if (force) {
up.doRPS = up.doBooters = up.doMisc = true;
} else if (!doAny) {
result = 0;
OSKextLog(NULL, kOSKextLogBasicLevel | kOSKextLogFileAccessFlag,
"%s: helper partitions appear up to date.", up.srcRoot);
goto finish;
}
if (strlcpy(up.root_uuid, up.caches->fsys_uuid, NCHARSUUID) > NCHARSUUID) {
result = EOVERFLOW;
OSKextLog(NULL, up.errLogSpec, "error copying root_uuid");
goto finish;
}
if ((result = updateBootHelpers(&up, expectUpToDate)))
goto finish;
if (statfs(up.srcRoot, &sfs) == 0) {
if ((sfs.f_flags & MNT_RDONLY) == 0) {
if ((result = updateStamps(up.caches, kBCStampsApplyTimes))) {
goto finish;
}
} else {
OSKextLog(NULL, up.warnLogSpec,
"Warning: %s is read-only: skipping bootstamp updates",
up.caches->root);
}
} else {
OSKextLog(NULL, up.errLogSpec, "statfs(%s): %s",
up.caches->root, strerror(errno));
goto finish;
}
skipHelperUpdates:
if (expectUpToDate) {
result = EX_OSFILE;
} else {
result = EX_OK;
}
finish:
if (up.earlyBoot) {
if (result != EX_OSFILE) {
if (takeVolumeForPath(up.srcRoot)) {
OSKextLog(NULL, up.errLogSpec, "kextd lock/signal failed");
}
}
}
if (result && result != EX_OSFILE) {
result = getExitValueFor(result);
}
cleanUpContext(&up, result);
return result;
}
#define kBRCheckLogSpec (kOSKextLogArchiveFlag | kOSKextLogProgressLevel)
OSStatus
BRUpdateBootFiles(CFURLRef volURL, Boolean force)
{
if (!volURL)
return EINVAL;
return checkUpdateCachesAndBoots(volURL, force,
false ,
false );
}
static OSStatus
mergeHostVolInfo(struct updatingVol *up, CFURLRef hostVol)
{
OSStatus result;
up->hostCaches = readBootCachesForVolURL(hostVol);
if (up->hostCaches) {
if (strlcpy(up->root_uuid,up->hostCaches->fsys_uuid,NCHARSUUID)>NCHARSUUID){
result = EOVERFLOW;
goto finish;
}
pathcpy(up->hostroot, up->hostCaches->root);
if (up->hostCaches->erpropcache) {
pathcpy(up->altERpropcache, up->hostCaches->erpropcache->rpath);
up->hostfd = open(up->hostroot, O_RDONLY);
if (up->hostfd == -1) goto finish;
}
} else {
uuid_t vol_uuid;
if (!CFURLGetFileSystemRepresentation(hostVol, true ,
(UInt8*)up->hostroot, PATH_MAX)) {
result = EOVERFLOW;
goto finish;
}
if ((result = copyVolumeUUIDs(up->hostroot, vol_uuid, NULL)))
goto finish;
uuid_unparse_upper(vol_uuid, up->root_uuid);
}
result = 0;
finish:
return result;
}
OSStatus
BRCopyBootFiles(CFURLRef srcVol,
CFURLRef hostVol,
CFStringRef helperBSDName,
CFDictionaryRef bootPrefOverrides)
{
OSStatus result; struct updatingVol up = { };
if (!srcVol || !hostVol || !helperBSDName) {
result = EINVAL;
goto finish;
}
result = initContext(&up, srcVol, helperBSDName, false );
if (result) goto finish;
result = checkRebuildAllCaches(up.caches, kBRCheckLogSpec);
if (result) goto finish;
(void)needUpdates(up.caches, NULL, NULL, NULL,
kOSKextLogGeneralFlag | kOSKextLogProgressLevel);
result = mergeHostVolInfo(&up, hostVol);
if (result) goto finish;
up.doRPS = up.doBooters = up.doMisc = true;
up.bootPrefOverrides = bootPrefOverrides;
result = updateBootHelpers(&up, false );
if (result == 0 && CFEqual(srcVol, hostVol)) {
result = updateStamps(up.caches, kBCStampsApplyTimes);
}
finish:
cleanUpContext(&up, result);
return result;
}
static int
FindRPSDir(struct updatingVol *up, char prev[PATH_MAX], char current[PATH_MAX],
char next[PATH_MAX])
{
char rpath[PATH_MAX], ppath[PATH_MAX], spath[PATH_MAX];
int rval = ELAST + 1, status;
struct stat r, p, s;
Boolean haveR = false, haveP = false, haveS = false;
char *prevp = NULL, *curp = NULL, *nextp = NULL;
pathcpy(rpath, up->curMount);
pathcat(rpath, "/");
pathcpy(ppath, rpath);
pathcpy(spath, rpath);
pathcat(rpath, kBootDirR);
pathcat(ppath, kBootDirP);
pathcat(spath, kBootDirS);
status = stat(rpath, &r); haveR = (status == 0);
status = stat(ppath, &p);
haveP = (status == 0);
status = stat(spath, &s);
haveS = (status == 0);
if (haveR && haveP && haveS) { OSKextLog(NULL, up->warnLogSpec,
"Warning: all of R,P,S exist: picking 'R'; destroying 'P'.");
curp = rpath; nextp = ppath; prevp = spath;
if ((rval = eraseRPS(up, nextp)))
goto finish;
} else if (haveR && haveP) { curp = ppath; nextp = spath; prevp = rpath;
} else if (haveR && haveS) {
curp = rpath; nextp = ppath; prevp = spath;
} else if (haveP && haveS) {
curp = spath; nextp = rpath; prevp = ppath;
} else if (haveR) { curp = rpath; nextp = ppath; prevp = spath;
} else if (haveP) {
curp = ppath; nextp = spath; prevp = rpath;
} else if (haveS) {
curp = spath; nextp = rpath; prevp = ppath;
} else { curp = rpath; nextp = ppath; prevp = spath;
}
if (strlcpy(prev, prevp, PATH_MAX) >= PATH_MAX) goto finish;
if (strlcpy(current, curp, PATH_MAX) >= PATH_MAX) goto finish;
if (strlcpy(next, nextp, PATH_MAX) >= PATH_MAX) goto finish;
rval = 0;
finish:
if (rval) {
OSKextLog(NULL, up->errLogSpec,
"%s - strlcpy or cat failed - >= PATH_MAX", __FUNCTION__);
}
return rval;
}
static int
sBLSetBootFinderInfo(struct updatingVol *up, uint32_t newvinfo[8])
{
int result, fd = -1;
uint32_t vinfo[8];
if (schdir(up->curbootfd, up->curMount, &fd)) goto finish;
result = BLGetVolumeFinderInfo(NULL, ".", vinfo);
if (result) goto finish;
vinfo[kSystemFolderIdx] = newvinfo[kSystemFolderIdx];
vinfo[kEFIBooterIdx] = newvinfo[kEFIBooterIdx];
result = BLSetVolumeFinderInfo(NULL, ".", vinfo);
finish:
if (fd != -1)
(void)restoredir(fd);
return result;
}
static int
blessRecovery(struct updatingVol *up)
{
int result;
char path[PATH_MAX];
struct stat sb;
uint32_t vinfo[8] = { 0, };
result = ENAMETOOLONG;
makebootpath(path, "/" kRecoveryBootDir);
if (stat(path, &sb) == -1) {
result = errno;
goto finish;
}
vinfo[kSystemFolderIdx] = (uint32_t)sb.st_ino;
pathcat(path, "/");
pathcat(path, basename(up->caches->efibooter.rpath));
if (stat(path, &sb) == -1) {
result = errno;
goto finish;
}
vinfo[kEFIBooterIdx] = (uint32_t)sb.st_ino;
if ((result = sBLSetBootFinderInfo(up, vinfo))) {
OSKextLog(NULL, up->warnLogSpec,
"Warning: found recovery booter but couldn't bless it.");
}
finish:
return result;
}
#define RECERR(up, opres, warnmsg) do { \
if (opres == -1 && errno == ENOENT) { \
opres = 0; \
} \
if (opres) { \
if (warnmsg) { \
OSKextLog(NULL, up->warnLogSpec, warnmsg); \
} \
if (firstErr == 0) { \
OSKextLog(NULL, up->warnLogSpec, "capturing err %d / %d", \
opres, errno); \
firstErr = opres; \
if (firstErr == -1) firstErrno = errno; \
} \
} \
} while(0)
OSStatus
BREraseBootFiles(CFURLRef srcVolRoot, CFStringRef helperBSDName)
{
OSStatus result; int opres, firstErrno, firstErr = 0;
struct updatingVol up = { }, *upp = &up;
char path[PATH_MAX], prevRPS[PATH_MAX], nextRPS[PATH_MAX];
uint32_t zerowords[8] = { 0, };
unsigned i;
if (!srcVolRoot || !helperBSDName) {
result = EINVAL;
goto finish;
}
result = initContext(&up, srcVolRoot, helperBSDName, false );
if (result) goto finish;
if ((result = mountBoot(&up))) {
goto finish; }
if ((opres = blessRecovery(&up))) {
if ((opres = sBLSetBootFinderInfo(&up, zerowords))) {
firstErr = opres;
OSKextLog(NULL, up.warnLogSpec,
"Warning: couldn't unbless %s", up.curMount);
}
}
opres = nukeBRLabels(&up);
RECERR(upp, opres,"Warning: trouble nuking (inactive?) Boot!=Root label.");
if (up.caches->ofbooter.rpath[0]) {
pathcpy(path, up.curMount);
pathcat(path, up.caches->ofbooter.rpath);
opres = sunlink(up.curbootfd, path);
RECERR(upp, opres, "couldn't unlink OF booter" );
}
if (up.caches->efibooter.rpath[0]) {
pathcpy(path, up.curMount);
pathcat(path, up.caches->efibooter.rpath);
opres = sunlink(up.curbootfd, path);
RECERR(upp, opres, "couldn't unlink EFI booter" );
}
opres = FindRPSDir(&up, prevRPS, up.curRPS, nextRPS);
if (opres == 0) {
opres = eraseRPS(&up, prevRPS);
RECERR(upp, opres, "Warning: trouble erasing R.");
opres = eraseRPS(&up, up.curRPS);
RECERR(upp, opres, "Warning: trouble erasing P.");
opres = eraseRPS(&up, nextRPS);
RECERR(upp, opres, "Warning: trouble erasing S.");
} else {
RECERR(upp, opres, "Warning: couldn't find RPS directions.");
}
for (i=0; i < up.caches->nmisc; i++) {
char *rpath = up.caches->miscpaths[i].rpath;
if (strlcpy(path, up.curMount, PATH_MAX) > PATH_MAX) continue;
if (strlcat(path, rpath, PATH_MAX) > PATH_MAX) continue;
opres = sdeepunlink(up.curbootfd, path);
RECERR(upp, opres, "error unlinking miscpath" );
}
unmountBoot(&up);
if (firstErr == -1) errno = firstErrno;
result = firstErr;
finish:
return result;
}
static int revertState(struct updatingVol *up)
{
int rval = 0; char path[PATH_MAX], oldpath[PATH_MAX];
struct bootCaches *caches = up->caches;
Boolean doMisc;
struct stat sb;
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag,
"Rolling back any incomplete updates.");
switch (up->changestate) {
case activatedBooters:
pathcat(up->ofdst, OLDEXT);
pathcat(up->efidst, OLDEXT);
rval |= activateBooters(up);
case activatingEFIBooter:
case activatingOFBooter: case copiedBooters:
case copyingEFIBooter:
if (caches->efibooter.rpath[0]) {
makebootpath(path, caches->efibooter.rpath);
pathcpy(oldpath, path); pathcat(oldpath, OLDEXT);
if (stat(oldpath, &sb) == 0) {
(void)sunlink(up->curbootfd, path);
rval |= srename(up->curbootfd, oldpath, path);
}
}
case copyingOFBooter:
if (caches->ofbooter.rpath[0]) {
makebootpath(path, caches->ofbooter.rpath);
pathcpy(oldpath, path);
pathcat(oldpath, OLDEXT);
if (stat(oldpath, &sb) == 0) {
(void)sunlink(up->curbootfd, path);
rval |= srename(up->curbootfd, oldpath, path);
}
}
case noLabel:
doMisc = up->doMisc;
up->doMisc = false;
rval |= activateMisc(up); up->doMisc = doMisc;
case nothingSerious:
break;
}
finish:
if (rval) {
OSKextLog(NULL, kOSKextLogErrorLevel,
"error rolling back incomplete updates.");
}
return rval;
};
static int mountBoot(struct updatingVol *up)
{
int rval = ELAST + 1;
CFStringRef mountargs[] = { CFSTR("perm"), CFSTR("nobrowse"), NULL };
CFStringRef str;
DADissenterRef dis = (void*)kCFNull;
CFDictionaryRef ddesc = NULL;
CFURLRef volURL;
struct statfs bsfs;
uint32_t mntgoal;
struct stat secsb;
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag,
"Mounting helper partition...");
str = (CFStringRef)CFArrayGetValueAtIndex(up->boots, up->bootIdx);
if (!str) {
goto finish;
}
if (!CFStringGetFileSystemRepresentation(str,up->bsdname,DEVMAXPATHSIZE)){
goto finish;
}
if (!(up->curBoot=DADiskCreateFromBSDName(nil,up->dasession,up->bsdname))){
goto finish;
}
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag,
"Mounted %s.", up->bsdname);
DADiskMountWithArguments(up->curBoot, NULL,kDADiskMountOptionDefault,
_daDone, &dis, mountargs);
if (dis == (void*)kCFNull) {
CFRunLoopRun(); }
if (dis) {
rval = DADissenterGetStatus(dis);
if (rval != kDAReturnBusy) {
goto finish;
}
}
if (!(ddesc = DADiskCopyDescription(up->curBoot))) goto finish;
volURL = CFDictionaryGetValue(ddesc, kDADiskDescriptionVolumePathKey);
if (!volURL || CFGetTypeID(volURL) != CFURLGetTypeID()) goto finish;
if (!CFURLGetFileSystemRepresentation(volURL, true ,
(UInt8*)up->curMount, PATH_MAX)) goto finish;
if (-1 == (up->curbootfd = open(up->curMount, O_RDONLY, 0))) goto finish;
if (fstat(up->caches->cachefd, &secsb)) goto finish;
if (fstatfs(up->curbootfd, &bsfs)) goto finish;
mntgoal = bsfs.f_flags;
mntgoal &= ~(MNT_RDONLY|MNT_IGNORE_OWNERSHIP);
if ((bsfs.f_flags != mntgoal) && updateMount(up->curMount, mntgoal)) {
OSKextLog(NULL, up->warnLogSpec,
"Warning: couldn't update mount to read/write + owners");
}
if (bsfs.f_blocks * bsfs.f_bsize < (128 * 1<<20)) {
OSKextLog(NULL, up->errLogSpec, "skipping Apple_Boot helper < 128 MB.");
goto finish;
}
rval = 0;
finish:
if (ddesc) CFRelease(ddesc);
if (dis && dis != (void*)kCFNull) { CFRelease(dis);
}
if (rval != 0 && up->curBoot) {
unmountBoot(up); }
if (rval) {
if (rval != ELAST + 1) {
OSKextLog(NULL, up->errLogSpec,
"Failed to mount helper partition: error %#X (DA err# %#.2x).",
rval, rval & ~(err_local|err_local_diskarbitration));
} else {
OSKextLog(NULL, up->errLogSpec,"Failed to mount helper partition.");
}
}
if (rval)
OSKextLog(NULL,kOSKextLogErrorLevel,"error mounting helper partition.");
return rval;
}
static void unmountBoot(struct updatingVol *up)
{
DADissenterRef dis = (void*)kCFNull;
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag,
"Unmounting helper partition %s.", up->bsdname);
if (!up->curBoot) goto finish;
if (!up->curMount[0]) goto finish;
if (up->curbootfd != -1) {
close(up->curbootfd);
up->curbootfd = -1;
}
DADiskUnmount(up->curBoot, kDADiskMountOptionDefault, _daDone, &dis);
if (dis == (void*)kCFNull) { CFRunLoopRun();
}
if (dis) {
OSKextLog(NULL, up->warnLogSpec, "%s didn't unmount, leaving mounted",
up->bsdname);
}
finish:
up->curMount[0] = '\0'; if (up->curBoot) {
CFRelease(up->curBoot);
up->curBoot = NULL;
}
if (dis && dis != (void*)kCFNull) {
CFRelease(dis);
}
}
static void
addDictValues(const void *key, const void *value, void *ctx)
{
CFMutableDictionaryRef tgtDict = (CFMutableDictionaryRef)ctx;
if (CFDictionaryContainsKey(tgtDict, key))
CFDictionaryRemoveValue(tgtDict, key);
CFDictionaryAddValue(tgtDict, key, value);
}
static int
insertUUID(struct updatingVol *up, char *srcpath, char *dstpath)
{
int rval = ELAST + 1;
int fd = -1;
struct stat sb;
sb.st_mode = kCacheFileMode; void *buf = NULL;
CFDataRef data = NULL;
CFMutableDictionaryRef pldict = NULL;
CFStringRef str = NULL;
mode_t dirmode;
char dstparent[PATH_MAX];
CFIndex len;
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag,
"Inserting filesystem UUID (%s) into Boot.plist.",
up->root_uuid);
do {
if (-1 == (fd=sopen(up->caches->cachefd,srcpath,O_RDONLY,0)))
break;
if (fstat(fd, &sb)) break;
if (sb.st_size > UINT_MAX || sb.st_size > LONG_MAX) break;
if (!(buf = malloc((size_t)sb.st_size))) break;
if (read(fd, buf, (size_t)sb.st_size) != sb.st_size)break;
if (!(data = CFDataCreate(nil, buf, (long)sb.st_size)))
break;
pldict =(CFMutableDictionaryRef)CFPropertyListCreateFromXMLData(nil,
data, kCFPropertyListMutableContainers, NULL);
} while(0);
if (!pldict || CFGetTypeID(pldict)!=CFDictionaryGetTypeID()) {
SAFE_RELEASE_NULL(pldict);
pldict = CFDictionaryCreateMutable(nil, 1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!pldict) goto finish;
}
str = CFStringCreateWithCString(nil, up->root_uuid, kCFStringEncodingASCII);
if (!str) goto finish;
CFDictionarySetValue(pldict, CFSTR(kRootUUIDKey), str);
if (up->bootPrefOverrides) {
CFDictionaryApplyFunction(up->bootPrefOverrides,addDictValues,pldict);
}
dirmode = ((sb.st_mode & ~S_IFMT) | S_IWUSR | S_IXUSR );
if (dirmode & S_IRGRP) dirmode |= S_IXGRP; if (dirmode & S_IROTH) dirmode |= S_IXOTH;
if (strlcpy(dstparent, dirname(dstpath), PATH_MAX) >= PATH_MAX) goto finish;
if ((sdeepmkdir(up->curbootfd, dstparent, dirmode))) goto finish;
(void)sunlink(up->curbootfd, dstpath);
close(fd); if (-1 == (fd=sopen(up->curbootfd, dstpath, O_WRONLY|O_CREAT,sb.st_mode))){
goto finish;
}
if (data) CFRelease(data);
if (!(data = CFPropertyListCreateXMLData(nil, pldict)))
goto finish;
len = CFDataGetLength(data);
if (write(fd, CFDataGetBytePtr(data), len) != len) goto finish;
rval = 0;
finish:
if (rval) {
OSKextLog(NULL, up->errLogSpec,
"%s - Error inserting UUID: %d %s.",
__FUNCTION__, errno, strerror(errno));
}
if (str) CFRelease(str);
if (pldict) CFRelease(pldict);
if (data) CFRelease(data);
if (buf) free(buf);
if (fd != -1) close(fd);
return rval;
}
static int
eraseRPS(struct updatingVol *up, char *toErase)
{
int rval = ELAST+1;
char *erpath = NULL;
char path[PATH_MAX];
struct stat sb;
if (stat(toErase, &sb) == -1 && errno == ENOENT) {
rval = 0;
goto finish;
}
if (up->altERpropcache[0]) {
erpath = up->altERpropcache;
} else if (up->caches->erpropcache) {
erpath = up->caches->erpropcache->rpath;
}
if (erpath) {
pathcpy(path, toErase);
pathcat(path, erpath);
if (szerofile(up->curbootfd, path))
goto finish;
}
rval = sdeepunlink(up->curbootfd, toErase);
finish:
if (rval) {
OSKextLog(NULL, up->errLogSpec | kOSKextLogFileAccessFlag,
"%s - %s. errno %d %s",
__FUNCTION__, up->curRPS, errno, strerror(errno));
}
return rval;
}
static int
ucopyRPS(struct updatingVol *up)
{
int bsderr, rval = ELAST+1;
char discard[PATH_MAX];
unsigned i;
char srcpath[PATH_MAX], dstpath[PATH_MAX];
char * plistNamePtr;
COMPILE_TIME_ASSERT(sizeof(BOOTPLIST_NAME)==sizeof(BOOTPLIST_APM_NAME));
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag,
"Copying files used by the booter.");
if (FindRPSDir(up, up->curRPS, discard, discard)) goto finish;
if (eraseRPS(up, up->curRPS))
goto finish;
if (smkdir(up->curbootfd, up->curRPS, kRPSDirMask)) {
OSKextLog(NULL, up->errLogSpec, "%s - mkdir failed for %s",
__FUNCTION__, up->curRPS);
goto finish;
}
for (i = 0; i < up->caches->nrps; i++) {
cachedPath *curItem = &up->caches->rpspaths[i];
pathcpy(srcpath, up->caches->root);
pathcat(srcpath, curItem->rpath);
pathcpy(dstpath, up->curRPS);
pathcat(dstpath, curItem->rpath);
if (curItem == up->caches->bootconfig) {
if (up->onAPM) {
plistNamePtr = strstr(dstpath, BOOTPLIST_NAME);
if (plistNamePtr) {
strncpy(plistNamePtr, BOOTPLIST_APM_NAME, strlen(BOOTPLIST_NAME));
}
}
if (insertUUID(up, srcpath, dstpath)) {
goto finish;
}
} else if (up->altERpropcache[0] && curItem==up->caches->erpropcache) {
continue;
} else {
bsderr=scopyitem(up->caches->cachefd,srcpath,up->curbootfd,dstpath);
if (bsderr) {
if ((curItem == up->caches->erpropcache ||
curItem == up->caches->efiloccache)
&& bsderr == -1 && errno == ENOENT) {
continue;
} else {
OSKextLog(NULL, up->errLogSpec,
"Error copying %s to %s",
srcpath, dstpath);
goto finish;
}
}
}
}
if (up->altERpropcache[0]) {
pathcpy(srcpath, up->hostroot);
pathcat(srcpath, up->altERpropcache);
pathcpy(dstpath, up->curRPS);
pathcat(dstpath, up->altERpropcache);
if ((bsderr = scopyitem(up->hostfd, srcpath, up->curbootfd, dstpath))
&& errno != ENOENT)
goto finish;
}
rval = 0;
finish:
if (rval) {
OSKextLog(NULL, up->errLogSpec,
"%s - Error copying files used by the booter. errno %d %s",
__FUNCTION__, errno, strerror(errno));
}
return rval;
}
static int ucopyMisc(struct updatingVol *up)
{
int bsderr, rval = -1;
unsigned i, nprocessed = 0;
char srcpath[PATH_MAX], dstpath[PATH_MAX];
struct stat sb;
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag,
"Copying files read before the booter runs.");
for (i = 0; i < up->caches->nmisc; i++) {
pathcpy(srcpath, up->caches->root);
pathcat(srcpath, up->caches->miscpaths[i].rpath);
pathcpy(dstpath, up->curMount);
pathcat(dstpath, up->caches->miscpaths[i].rpath);
pathcat(dstpath, ".new");
if (stat(srcpath, &sb) == 0) {
if ((bsderr = scopyitem(up->caches->cachefd, srcpath,
up->curbootfd, dstpath))) {
OSKextLog(NULL, up->errLogSpec,
"Error copying %s to %s",
srcpath, dstpath);
continue;
}
} else if (errno != ENOENT) {
continue;
}
nprocessed++;
}
rval = (nprocessed != i);
finish:
if (rval) {
OSKextLog(NULL, up->errLogSpec,
"%s - Error copying files read before the booter runs. errno %d %s",
__FUNCTION__, errno, strerror(errno));
}
return rval;
}
static int moveLabels(struct updatingVol *up)
{
int rval = -1;
char path[PATH_MAX];
struct stat sb;
int fd = -1;
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag,
"Moving aside old label.");
pathcpy(path, up->curMount);
pathcat(path, up->caches->label->rpath);
if (0 == (stat(path, &sb))) {
char newpath[PATH_MAX];
unsigned char nulltype[32] = {'\0', };
pathcpy(newpath, path);
pathcat(newpath, NEWEXT);
rval = srename(up->curbootfd, path, newpath);
if (rval) goto finish;
if (-1 == (fd=sopen(up->curbootfd, newpath, O_RDWR, 0))) goto finish;
if(fsetxattr(fd,XATTR_FINDERINFO_NAME,&nulltype,sizeof(nulltype),0,0)) {
goto finish;
}
}
up->changestate = noLabel;
rval = 0;
finish:
if (fd != -1) close(fd);
if (rval) {
OSKextLog(NULL, up->errLogSpec,
"%s - Error moving aside old label. errno %d %s.",
__FUNCTION__, errno, strerror(errno));
}
return rval;
}
static int
nukeBRLabels(struct updatingVol *up)
{
int rval; int opres, firstErrno, firstErr = 0;
char labelp[PATH_MAX];
struct stat sb;
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag,
"Removing current disk label.");
pathcpy(labelp, up->curMount);
pathcat(labelp, up->caches->label->rpath);
if (0 == (stat(labelp, &sb))) {
opres = sunlink(up->curbootfd, labelp);
RECERR(up, opres, "error removing label" );
} else {
errno = 0;
}
pathcat(labelp, CONTENTEXT);
if (0 == (stat(labelp, &sb))) {
opres = sunlink(up->curbootfd, labelp);
RECERR(up, opres, "error removing .contentDetails" );
} else {
errno = 0;
}
up->changestate = noLabel;
if (firstErr == -1) errno = firstErrno;
rval = firstErr;
finish:
if (rval)
OSKextLog(NULL, kOSKextLogErrorLevel, "Error removing disk label.");
return rval;
}
static int ucopyBooters(struct updatingVol *up)
{
int rval = ELAST + 1;
int bsderr;
char srcpath[PATH_MAX], oldpath[PATH_MAX];
int nbooters = 0;
if (up->caches->ofbooter.rpath[0]) nbooters++;
if (up->caches->efibooter.rpath[0]) nbooters++;
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag,
"Copying new booter%s.", nbooters == 1 ? "" : "s");
up->changestate = copyingOFBooter;
if (up->caches->ofbooter.rpath[0]) {
pathcpy(srcpath, up->caches->root);
pathcat(srcpath, up->caches->ofbooter.rpath); pathcpy(up->ofdst, up->curMount);
pathcat(up->ofdst, up->caches->ofbooter.rpath); pathcpy(oldpath, up->ofdst);
pathcat(oldpath, OLDEXT);
(void)sunlink(up->curbootfd, oldpath);
bsderr = srename(up->curbootfd, up->ofdst, oldpath);
if (bsderr && errno !=ENOENT) {
OSKextLog(NULL, up->errLogSpec,
"%s - Error rename old %s new %s",
__FUNCTION__, up->ofdst, oldpath);
goto finish;
}
if ((bsderr = scopyitem(up->caches->cachefd, srcpath,
up->curbootfd, up->ofdst))) {
OSKextLog(NULL, up->errLogSpec, "%s - Error copying %s to %s",
__FUNCTION__, srcpath, up->ofdst);
goto finish;
}
}
up->changestate = copyingEFIBooter;
if (up->caches->efibooter.rpath[0]) {
pathcpy(srcpath, up->caches->root);
pathcat(srcpath, up->caches->efibooter.rpath); pathcpy(up->efidst, up->curMount);
pathcat(up->efidst, up->caches->efibooter.rpath);
pathcpy(oldpath, up->efidst);
pathcat(oldpath, OLDEXT);
(void)sunlink(up->curbootfd, oldpath);
bsderr = srename(up->curbootfd, up->efidst, oldpath);
if (bsderr && errno != ENOENT) {
OSKextLog(NULL, up->errLogSpec,
"%s - Error rename old %s new %s",
__FUNCTION__, up->efidst, oldpath);
goto finish;
}
if ((bsderr = scopyitem(up->caches->cachefd, srcpath,
up->curbootfd, up->efidst))) {
OSKextLog(NULL, up->errLogSpec, "%s - Error copying %s to %s",
__FUNCTION__, srcpath, up->efidst);
goto finish;
}
}
up->changestate = copiedBooters;
rval = 0;
finish:
if (rval)
OSKextLog(NULL, up->errLogSpec,
"%s - Error copying booter. errno %d %s",
__FUNCTION__, errno, strerror(errno));
return rval;
}
#define CLOSE(fd) do { (void)close(fd); fd = -1; } while(0)
static int activateBooters(struct updatingVol *up)
{
int rval = ELAST + 1;
int fd = -1;
uint32_t vinfo[8] = { 0, };
struct stat sb;
char parent[PATH_MAX];
int nbooters = 0;
if (up->caches->ofbooter.rpath[0]) nbooters++;
if (up->caches->efibooter.rpath[0]) nbooters++;
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag,
"Activating new booter%s.", nbooters == 1 ? "" : "s");
up->changestate = activatingOFBooter;
if (up->caches->ofbooter.rpath[0]) {
unsigned char tbxichrp[32] = {'t','b','x','i','c','h','r','p','\0',};
if (-1 == (fd=sopen(up->curbootfd, up->ofdst, O_RDWR, 0))) goto finish;
if (fcntl(fd, F_FULLFSYNC)) goto finish;
if(fsetxattr(fd,XATTR_FINDERINFO_NAME,&tbxichrp,sizeof(tbxichrp),0,0)) {
goto finish;
}
CLOSE(fd);
pathcpy(parent, dirname(up->ofdst));
if (-1 == (fd=sopen(up->curbootfd, parent, O_RDONLY, 0))) goto finish;
if (fstat(fd, &sb)) goto finish;
CLOSE(fd);
if (sb.st_ino < (__darwin_ino64_t)2<<31) {
vinfo[kSystemFolderIdx] = (uint32_t)sb.st_ino;
} else {
rval = EOVERFLOW;
goto finish;
}
}
up->changestate = activatingEFIBooter;
if (up->caches->efibooter.rpath[0]) {
if (-1==(fd=sopen(up->curbootfd, up->efidst, O_RDONLY, 0))) goto finish;
if (fcntl(fd, F_FULLFSYNC)) goto finish;
if (fstat(fd, &sb)) goto finish;
CLOSE(fd);
if (sb.st_ino < (__darwin_ino64_t)2<<31) {
vinfo[kEFIBooterIdx] = (uint32_t)sb.st_ino;
} else {
rval = EOVERFLOW;
goto finish;
}
if (!vinfo[0]) {
pathcpy(parent, dirname(up->efidst));
if (-1 == (fd=sopen(up->curbootfd, parent, O_RDONLY, 0))) {
goto finish;
}
if (fstat(fd, &sb)) goto finish;
CLOSE(fd);
if (sb.st_ino < (__darwin_ino64_t)2<<31) {
vinfo[kSystemFolderIdx] = (uint32_t)sb.st_ino;
} else {
rval = EOVERFLOW;
goto finish;
}
}
}
if ((rval = sBLSetBootFinderInfo(up, vinfo))) goto finish;
up->changestate = activatedBooters;
finish:
if (fd != -1) close(fd);
if (rval)
OSKextLog(NULL, kOSKextLogErrorLevel, "Error activating booter.");
return rval;
}
static int activateRPS(struct updatingVol *up)
{
int rval = ELAST + 1;
char prevRPS[PATH_MAX], curRPS[PATH_MAX], nextRPS[PATH_MAX];
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag,
"Activating files used by the booter.");
if (FindRPSDir(up, prevRPS, curRPS, nextRPS)) goto finish;
if (strncmp(curRPS, up->curRPS, PATH_MAX) != 0) {
if (srename(up->curbootfd, prevRPS, nextRPS)) goto finish;
}
if (fcntl(up->curbootfd, F_FULLFSYNC)) goto finish;
rval = 0;
finish:
if (rval) {
OSKextLog(NULL, kOSKextLogErrorLevel,
"Error activating files used by the booter.");
}
return rval;
}
#ifndef OPENSOURCE // BLGenerateOFLabel uses CG
static int writeLabels(struct updatingVol *up, char *labelpath)
{
int temp_err, rval = ELAST + 1;
CFDataRef lData = NULL;
CFIndex len;
int fd = -1;
char bootname[NAME_MAX];
char labeldir[PATH_MAX], contentPath[PATH_MAX];
char *fmt = (CFArrayGetCount(up->boots) == 1) ? "%s" : "%s %d";
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag,
"Writing new disk label.");
if (NAME_MAX <= snprintf(bootname, NAME_MAX, fmt,
up->caches->volname, up->bootIdx + 1))
goto finish;
temp_err = BLGenerateOFLabel(NULL, bootname, &lData);
if (temp_err) {
OSKextLog(NULL, up->errLogSpec,
"%s - BLGenerateOFLabel with err %d",
__FUNCTION__, temp_err);
goto finish;
}
pathcpy(labeldir, dirname(labelpath));
if (-1 == sdeepmkdir(up->curbootfd, labeldir, kCacheDirMode))
goto finish;
fd = sopen(up->curbootfd, labelpath, O_CREAT|O_WRONLY, kCacheFileMode);
if (fd == -1) goto finish;
len = CFDataGetLength(lData);
if (write(fd, CFDataGetBytePtr(lData), len) != len) goto finish;
pathcpy(contentPath, labelpath);
pathcat(contentPath, CONTENTEXT);
close(fd);
fd = sopen(up->curbootfd, contentPath, O_CREAT|O_WRONLY, kCacheFileMode);
if (fd == -1) goto finish;
len = strlen(bootname);
if (write(fd, bootname, len) != len) goto finish;
rval = 0;
finish:
if (rval)
OSKextLog(NULL, up->errLogSpec,
"%s - Warning: trouble writing disk label: %d %s.",
__FUNCTION__, errno, strerror(errno));
if (fd != -1) close(fd);
if (lData) CFRelease(lData);
#ifdef WHEN_9028349_FIXED
return rval;
#else
return 0;
#endif
}
#endif // OPENSOURCE
static int activateMisc(struct updatingVol *up) {
int rval = ELAST + 1;
char path[PATH_MAX], opath[PATH_MAX];
unsigned i = 0, nprocessed = 0;
int fd = -1;
struct stat sb;
unsigned char tbxjchrp[32] = { 't','b','x','j','c','h','r','p','\0', };
if (up->doMisc) {
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag,
"Activating files used before the booter runs.");
for (i = 0; i < up->caches->nmisc; i++) {
if (strlcpy(path, up->curMount, PATH_MAX) >= PATH_MAX) continue;
if (strlcat(path, up->caches->miscpaths[i].rpath, PATH_MAX)
> PATH_MAX) continue;
if (strlcpy(opath, path, PATH_MAX) >= PATH_MAX) continue;
if (strlcat(opath, NEWEXT, PATH_MAX) >= PATH_MAX) continue;
if (stat(opath, &sb) == 0) {
if (srename(up->curbootfd, opath, path)) continue;
}
nprocessed++;
}
}
pathcpy(path, up->curMount);
pathcat(path, up->caches->label->rpath);
#ifndef OPENSOURCE
if (up->expectUpToDate) {
#endif
char newpath[PATH_MAX];
pathcpy(newpath, path); pathcat(newpath, NEWEXT);
(void)srename(up->curbootfd, newpath, path);
#ifndef OPENSOURCE
} else {
(void)sunlink(up->curbootfd, path);
if (writeLabels(up, path)) goto finish;
#endif
}
if (0 == (stat(path, &sb))) {
if (-1 == (fd = sopen(up->curbootfd, path, O_RDWR, 0))) goto finish;
if (fsetxattr(fd,XATTR_FINDERINFO_NAME,&tbxjchrp,sizeof(tbxjchrp),0,0))
goto finish;
close(fd); fd = -1;
}
rval = (i != nprocessed);
finish:
if (fd != -1) close(fd);
if (rval) {
OSKextLog(NULL, kOSKextLogErrorLevel,
"Error activating files used before the booter runs.");
}
return rval;
}
static int nukeFallbacks(struct updatingVol *up)
{
int rval = 0; int bsderr;
char delpath[PATH_MAX];
struct bootCaches *caches = up->caches;
OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag,
"Cleaning up fallbacks.");
if (!up->curBoot) goto finish;
if (up->doBooters) {
if (caches->ofbooter.rpath[0]) {
makebootpath(delpath, caches->ofbooter.rpath);
pathcat(delpath, OLDEXT);
if ((bsderr = sunlink(up->curbootfd, delpath)) && errno != ENOENT) {
rval |= bsderr;
}
}
if (caches->efibooter.rpath[0]) {
makebootpath(delpath, caches->efibooter.rpath);
pathcat(delpath, OLDEXT);
if ((bsderr = sunlink(up->curbootfd, delpath)) && errno != ENOENT) {
rval |= bsderr;
}
}
}
if (up->doRPS) {
char ignore[PATH_MAX];
if (0 == FindRPSDir(up, delpath, ignore, ignore)) {
rval |= eraseRPS(up, delpath);
}
}
finish:
if (rval)
OSKextLog(NULL, kOSKextLogErrorLevel, "Error cleaning up fallbacks.");
return rval;
}
static int kill_kextd(void)
{
int result = -1;
kern_return_t kern_result = kOSReturnError;
mach_port_t bootstrap_port = MACH_PORT_NULL;
mach_port_t kextd_port = MACH_PORT_NULL;
int kextd_pid = -1;
kern_result = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
if (kern_result != kOSReturnSuccess) {
goto finish;
}
kern_result = bootstrap_look_up(bootstrap_port,
(char *)KEXTD_SERVER_NAME, &kextd_port);
if (kern_result != kOSReturnSuccess) {
goto finish;
}
kern_result = pid_for_task(kextd_port, &kextd_pid);
if (kern_result != kOSReturnSuccess) {
goto finish;
}
result = kill(kextd_pid, SIGKILL);
if (-1 == result) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"kill kextd failed - %s.", strerror(errno));
}
finish:
if (kern_result != kOSReturnSuccess) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"kill kextd failed - %s.", safe_mach_error_string(kern_result));
}
return result;
}
int renameBootcachesPlist(
char * hostVolume,
char * oldPlistPath,
char * newPlistPath)
{
int result = -1;
int bootcachesPlistFd = -1;
char * errorMessage = NULL;
char * errorPath = NULL;
char oldname[PATH_MAX];
char newname[PATH_MAX];
char * kextcacheArgs[] = {
"/usr/sbin/kextcache",
"-f",
"-u",
NULL, NULL };
errorMessage = "path concatenation error";
errorPath = hostVolume;
if (strlcpy(oldname, hostVolume, PATH_MAX) >= PATH_MAX) {
goto finish;
}
if (strlcpy(newname, hostVolume, PATH_MAX) >= PATH_MAX) {
goto finish;
}
errorPath = oldPlistPath;
if (strlcpy(oldname, oldPlistPath, PATH_MAX) >= PATH_MAX) {
goto finish;
}
errorPath = newPlistPath;
if (strlcpy(newname, newPlistPath, PATH_MAX) >= PATH_MAX) {
goto finish;
}
errorPath = oldname;
bootcachesPlistFd = open(oldname, O_RDONLY);
if (-1 == bootcachesPlistFd) {
errorMessage = strerror(errno);
goto finish;
}
if (-1 == srename(bootcachesPlistFd, oldname, newname)) {
errorMessage = "rename failed.";
goto finish;
}
errorMessage = "couldn't kill kextd";
if (-1 == kill_kextd()) {
goto finish;
}
kextcacheArgs[3] = hostVolume;
result = fork_program(kextcacheArgs[0], kextcacheArgs, true );
finish:
if (errorMessage) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s - %s.", errorPath, errorMessage);
}
if (bootcachesPlistFd >= 0) {
close(bootcachesPlistFd);
}
return result;
}
#if 0
int activateBootFiles(
char * hostVolume,
char * sourceVolume,
CFPropertyListRef efiBootPlist)
{
int result = -1;
char * errorMessage = NULL;
char * errorPath = NULL;
struct updatingVol up = { NULL, };
if (renameBootcachesPlist(hostVolume, kBootCachesPath,
kBootCachesDisabledPath)) {
goto finish;
}
if (-1 == kill_kextd()) {
goto finish;
}
up.caches = readBootCachesForVolPath(sourceVolume);
if (!up.caches) {
goto finish;
}
errorMessage = "path concatenation error";
errorPath = hostVolume;
if (strlcpy(up.caches->root, hostVolume, sizeof(up.caches->root)) > sizeof(up.caches->root)) {
goto finish;
}
errorMessage = NULL;
errorPath = NULL;
up.efiBootPlist = efiBootPlist;
if (!hasBootRootBoots(up.caches, &up.boots,
NULL, &up.onAPM))
{
goto finish;
}
errorMessage = "path concatenation error";
errorPath = hostVolume;
if (strlcpy(up.caches->root, sourceVolume, sizeof(up.caches->root)) >
sizeof(up.caches->root))
{
goto finish;
}
errorMessage = NULL;
errorPath = NULL;
errorMessage = "trouble updating one or more helper partitions";
errorPath = hostVolume;
result = updateBootHelpers(&up, FALSE);
if (result != 0) {
goto finish;
}
errorMessage = NULL;
errorPath = NULL;
finish:
if (errorMessage) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s - %s.", errorPath, errorMessage);
}
SAFE_RELEASE(up.boots);
if (up.caches) destroyCaches(up.caches);
return result;
}
#endif
int revertBootFiles(char * hostVolume)
{
int result = -1;
char * errorMessage = NULL;
char * errorPath = NULL;
if (renameBootcachesPlist(hostVolume, kBootCachesDisabledPath,
kBootCachesPath)) {
goto finish;
}
(void)launch_rebuild_all(hostVolume, true, true);
result = 0;
finish:
if (errorMessage) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s - %s.", errorPath, errorMessage);
}
return result;
}
static int upstat(const char *path, struct stat *sb, struct statfs *sfs)
{
int rval = ELAST+1;
char buf[PATH_MAX], *tpath = buf;
struct stat defaultsb;
if (strlcpy(buf, path, PATH_MAX) > PATH_MAX) goto finish;
if (!sb) sb = &defaultsb;
while ((rval = stat(tpath, sb)) == -1 && errno == ENOENT) {
if (tpath[0] == '.' && tpath[1] == '\0') goto finish;
if (tpath[0] == '/' && tpath[1] == '\0') goto finish;
tpath = dirname(tpath); }
if (sfs)
rval = statfs(tpath, sfs);
finish:
if (rval) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogFileAccessFlag,
"Couldn't find volume for %s.", path);
}
return rval;
}
#define WAITFORLOCK 1
int takeVolumeForPath(const char *path)
{
int rval = ELAST + 1;
kern_return_t macherr = KERN_SUCCESS;
int lckres = 0;
struct statfs sfs;
const char *volPath = "<unknown>"; mach_port_t taskport = MACH_PORT_NULL;
if (sBRUptLock) {
return EALREADY; }
if (geteuid() != 0) {
OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
"Warning: non-root can't lock the volume for %s", path);
rval = 0;
goto finish;
}
if (!sKextdPort) {
macherr=bootstrap_look_up(bootstrap_port,KEXTD_SERVER_NAME,&sKextdPort);
if (macherr) goto finish;
}
if ((rval = upstat(path, NULL, &sfs))) goto finish;
volPath = sfs.f_mntonname;
if ((rval = copyVolumeUUIDs(volPath, s_vol_uuid, NULL))) {
goto finish;
}
taskport = mach_task_self();
if (taskport == MACH_PORT_NULL) goto finish;
macherr = mach_port_allocate(taskport,MACH_PORT_RIGHT_RECEIVE,&sBRUptLock);
if (macherr) goto finish;
macherr = kextmanager_lock_volume(sKextdPort, sBRUptLock, s_vol_uuid,
!WAITFORLOCK, &lckres);
if (macherr) goto finish;
while (lckres == EAGAIN) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
"kextd wasn't ready; waiting 10 seconds and trying again.");
sleep(10);
macherr = kextmanager_lock_volume(sKextdPort, sBRUptLock, s_vol_uuid,
!WAITFORLOCK, &lckres);
if (macherr) goto finish;
}
while (lckres == EBUSY) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
"%s locked; waiting for lock.", volPath);
macherr = kextmanager_lock_volume(sKextdPort, sBRUptLock, s_vol_uuid,
WAITFORLOCK, &lckres);
if (macherr) goto finish;
if (lckres == 0) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
"Lock acquired; proceeding.");
}
}
if (lckres == ENOENT) {
struct stat sb;
rval = stat(volPath, &sb);
if (rval == 0) {
OSKextLog(NULL, kOSKextLogProgressLevel | kOSKextLogGeneralFlag,
"WARNING: kextd not watching %s; proceeding w/o lock", volPath);
}
} else {
rval = lckres;
}
finish:
if (sBRUptLock != MACH_PORT_NULL && (lckres != 0 || macherr)) {
mach_port_mod_refs(taskport, sBRUptLock, MACH_PORT_RIGHT_RECEIVE, -1);
sBRUptLock = MACH_PORT_NULL;
}
if (macherr == BOOTSTRAP_UNKNOWN_SERVICE) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
"WARNING: kextd unavailable; proceeding w/o lock for %s", volPath);
rval = 0;
} else if (macherr) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
"Couldn't lock %s: %s (%d).", path,
safe_mach_error_string(macherr), macherr);
rval = macherr;
} else {
if (rval == -1) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogFileAccessFlag,
"Couldn't lock %s.", path);
rval = errno;
} else if (rval) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
"Couldn't lock %s: %s", path, strerror(rval));
}
}
return rval;
}
int putVolumeForPath(const char *path, int status)
{
int rval = KERN_SUCCESS;
if (sBRUptLock == MACH_PORT_NULL)
goto finish;
rval = kextmanager_unlock_volume(sKextdPort,sBRUptLock,s_vol_uuid,status);
mach_port_mod_refs(mach_task_self(),sBRUptLock,MACH_PORT_RIGHT_RECEIVE,-1);
sBRUptLock = MACH_PORT_NULL;
finish:
if (rval) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
"Couldn't unlock volume for %s: %s (%d).",
path, safe_mach_error_string(rval), rval);
}
return rval;
}