#include <bless.h>
#include <err.h>
#include <fcntl.h>
#include <libgen.h>
#include <sysexits.h>
#include <sys/mount.h>
#include <sys/xattr.h>
#include <unistd.h>
#include <IOKit/kext/kextmanager_types.h>
#include <bootfiles.h>
#include <CoreFoundation/CoreFoundation.h>
#include <TargetConditionals.h>
#if TARGET_OS_EMBEDDED
#define kDAReturnBusy 2
#define err_local_diskarbitration err_sub( 0x368 )
#else
#include <DiskArbitration/DiskArbitration.h>
#include <DiskArbitration/DiskArbitrationPrivate.h>
#endif
#include "bootcaches.h"
#include "logging.h"
#include "safecalls.h"
#include "update_boot.h"
#include "utility.h" // g_verbose_level
enum bootReversions {
nothingSerious = 0,
noLabels, copyingOFBooter, copyingEFIBooter, copiedBooters, activatingOFBooter, activatingEFIBooter, activatedBooters };
const char * bootReversionsStrings[] = {
NULL, "Labels deleted",
"Unlinking and copying BootX booter",
"Unlinking and copying EFI booter",
"Booters copied",
"Activating BootX",
"Activating EFI booter",
"Booters activated"
};
#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"
struct updatingVol {
struct bootCaches *caches; Boolean doRPS, doMisc, doBooters; Boolean expectUpToDate; enum bootReversions changestate; CFArrayRef boots; DASessionRef dasession;
int bootIdx; char bsdname[DEVMAXPATHSIZE]; DADiskRef curBoot; char curMount[MNAMELEN]; int curbootfd; char curRPS[PATH_MAX]; char efidst[PATH_MAX], ofdst[PATH_MAX];
Boolean isGPT;
};
static int mountBoot(struct updatingVol *up);
static int 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 nukeLabels(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 revertState(struct updatingVol *up);
#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 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;
}
int updateBoots(char *volRoot, int filec, const char *files[],
Boolean force, Boolean expectUpToDate)
{
int rval = ELAST + 1;
char *errmsg;
struct updatingVol up = { NULL, };
up.curbootfd = -1;
struct stat sb;
CFURLRef volURL = NULL;
DADiskRef dadisk = NULL;
CFIndex bootcount, bootupdates = 0;
Boolean doAny;
errmsg = "error getting description from Disk Arbitration";
#if TARGET_OS_EMBEDDED
goto finish;
#else
up.dasession = DASessionCreate(nil);
if (!up.dasession) goto finish;
#endif
volURL = CFURLCreateFromFileSystemRepresentation(nil, (UInt8*)volRoot,
strlen(volRoot) + 1, true );
if (!volURL) goto finish;
#if !TARGET_OS_EMBEDDED
dadisk = DADiskCreateFromVolumePath(nil, up.dasession, volURL);
#endif
if (!dadisk) goto finish;
errmsg = NULL; if (!(up.caches = readCaches(dadisk))) {
if (g_verbose_level)
kextd_log("%s: no "kBootCachesPath"; nothing to do", volRoot);
rval = 0;
goto finish;
}
if (!expectUpToDate) {
if (takeVolumeForPaths(volRoot, filec, files)) goto finish;
} else if (getppid() != 2 ) {
kextd_log("WARNING: no locks preventing kextd-triggered updates");
}
if (check_mkext(up.caches)) {
if (g_verbose_level > 2)
kextd_log("Rebuilding out-of-date mkext");
setenv("_com_apple_kextd_skiplocks", "1", 1); if (rebuild_mkext(up.caches, true ) != 0)
goto finish;
}
errmsg = "couldn't get Apple_Boot information";
if (!hasBoots(up.caches->bsdname, &up.boots, &up.isGPT)) {
rval = 0; if (g_verbose_level > 0)
kextd_log("%s: no helper partitions to update", volRoot);
goto finish;
}
if (fstat(up.caches->cachefd, &sb)) goto finish;
if (!up.isGPT && up.caches->ofbooter.rpath[0] == '\0') {
rval = 0;
if (g_verbose_level > 0)
kextd_log("%s only supports GPT-based helper partitions", volRoot);
goto finish;
}
errmsg = "trouble analyzing what needs updating";
if (needUpdates(up.caches, &doAny, &up.doRPS, &up.doBooters, &up.doMisc))
goto finish;
if (!doAny && !force) {
rval = 0;
if (g_verbose_level > 0)
kextd_log("%s: helper partitions appear up to date", volRoot);
goto finish;
}
if (force) up.doRPS = up.doBooters = up.doMisc = true;
up.expectUpToDate = expectUpToDate;
errmsg = "trouble updating one or more helper partitions";
#if !TARGET_OS_EMBEDDED
DASessionScheduleWithRunLoop(up.dasession, CFRunLoopGetCurrent(),
kCFRunLoopDefaultMode);
#endif
bootcount = CFArrayGetCount(up.boots);
for (up.bootIdx = 0; up.bootIdx < bootcount; up.bootIdx++) {
up.changestate = nothingSerious; if ((mountBoot(&up))) goto bootfail; if (up.doRPS && ucopyRPS(&up)) goto bootfail; if (up.doMisc) (void) ucopyMisc(&up);
if (expectUpToDate) {
if (moveLabels(&up)) goto bootfail;
} else {
if (nukeLabels(&up)) goto bootfail;
}
if (up.doBooters && ucopyBooters(&up)) goto bootfail;
if (up.doBooters && activateBooters(&up)) goto bootfail;
if (up.doRPS && activateRPS(&up)) goto bootfail;
if (activateMisc(&up))
goto bootfail;
up.changestate = nothingSerious;
bootupdates++; if (g_verbose_level > 0) {
kextd_log("Successfully updated helper partition %s", up.bsdname);
}
bootfail:
if (g_verbose_level > 0 && up.changestate != nothingSerious) {
kextd_error_log("error updating helper partition %s, state %d: %s", up.bsdname,
up.changestate, bootReversionsStrings[up.changestate]);
}
(void)revertState(&up);
if (nukeFallbacks(&up))
kextd_error_log("helper partition %s may be untidy", up.bsdname);
if (up.curBoot && unmountBoot(&up))
kextd_error_log("couldn't unmount helper partition %s", up.bsdname);
}
if (bootupdates != bootcount) goto finish;
errmsg = "trouble updating bootstamps";
if (applyStamps(up.caches)) goto finish;
if (expectUpToDate) {
rval = EX_OSFILE;
} else {
rval = EX_OK;
}
finish:
if (rval && rval != EX_OSFILE)
rval = getExitValueFor(rval);
putVolumeForPath(volRoot, rval);
if (volURL) CFRelease(volURL);
if (dadisk) CFRelease(dadisk);
if (up.boots) CFRelease(up.boots);
if (up.curbootfd != -1) close(up.curbootfd);
if (up.dasession) {
#if !TARGET_OS_EMBEDDED
DASessionUnscheduleFromRunLoop(up.dasession, CFRunLoopGetCurrent(),
kCFRunLoopDefaultMode);
#endif
CFRelease(up.dasession);
}
if (up.caches) destroyCaches(up.caches);
if (rval && rval != EX_OSFILE && errmsg) {
warnx("%s: %s", volRoot, errmsg);
}
return rval;
}
static int revertState(struct updatingVol *up)
{
int rval = 0; char path[PATH_MAX], oldpath[PATH_MAX];
struct bootCaches *caches = up->caches;
Boolean doMisc;
if (g_verbose_level > 2) kextd_log("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);
(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);
(void)sunlink(up->curbootfd, path);
rval |= srename(up->curbootfd, oldpath, path);
}
case noLabels:
doMisc = up->doMisc;
up->doMisc = false;
rval |= activateMisc(up); up->doMisc = doMisc;
case nothingSerious:
break;
}
finish:
return rval;
};
static int mountBoot(struct updatingVol *up)
{
int rval = ELAST + 1;
#if !TARGET_OS_EMBEDDED
CFStringRef mountargs[] = { CFSTR("perm"), CFSTR("nobrowse"), NULL };
CFStringRef str;
DADissenterRef dis = (void*)kCFNull;
CFDictionaryRef ddesc = NULL;
CFURLRef volURL;
struct statfs bsfs;
struct stat secsb;
if (g_verbose_level > 2) kextd_log("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;
if (g_verbose_level > 2) kextd_log("%s mounted", up->bsdname);
DADiskMountWithArguments(up->curBoot, NULL,kDADiskMountOptionDefault,
_daDone, &dis, mountargs);
if (dis == (void*)kCFNull)
CFRunLoopRun(); if (dis) {
rval = DADissenterGetStatus(dis);
if (rval == kDAReturnBusy && up->curMount[0] != '\1') {
up->curMount[0] = '\1';
if (0 == unmountBoot(up)) {
return mountBoot(up);
}
}
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;
if (bsfs.f_blocks * bsfs.f_bsize < (128 * 1<<20)) {
kextd_error_log("Apple_Boot helper < 128 MB; skipping");
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)
kextd_error_log("couldn't mount helper: error %#X (DA err# %#.2x)",
rval,rval & ~(err_local|err_local_diskarbitration));
else
kextd_error_log("couldn't mount helper partition");
}
#endif
return rval;
}
static int unmountBoot(struct updatingVol *up)
{
int rval = 0;
#if !TARGET_OS_EMBEDDED
DADissenterRef dis = (void*)kCFNull;
if (g_verbose_level > 2) kextd_log("Unmounting helper partition %s", up->bsdname);
if (!up->curBoot) goto finish;
if (!up->curMount[0]) goto finish;
if (up->curbootfd != -1) close(up->curbootfd);
rval = ELAST + 1;
DADiskUnmount(up->curBoot, kDADiskMountOptionDefault, _daDone, &dis);
if (dis == (void*)kCFNull) CFRunLoopRun();
if (dis) {
CFRelease(dis);
dis = (void*)kCFNull;
kextd_log("trouble unmounting boot partition; forcing...");
DADiskUnmount(up->curBoot, kDADiskUnmountOptionForce, _daDone, &dis);
if (dis == (void*)kCFNull)
CFRunLoopRun();
if (dis) goto finish;
}
rval = 0;
finish:
up->curMount[0] = '\0'; if (up->curBoot) {
CFRelease(up->curBoot);
up->curBoot = NULL;
}
if (dis && dis != (void*)kCFNull)
CFRelease(dis);
#endif
return rval;
}
typedef int EFI_STATUS;
typedef struct stat EFI_FILE_HANDLE;
typedef char UINT16;
typedef Boolean BOOLEAN;
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, haveP, haveS;
char *prevp, *curp, *nextp;
haveR = haveP = haveS = false;
prevp = curp = 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) { kextd_log("WARNING: all of R,P,S exist: picking 'R'\n");
curp = rpath; nextp = ppath; prevp = spath;
} 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)
kextd_error_log("FindRPSDir(): %s", strerror(errno));
return rval;
}
static int insertUUID(struct updatingVol *up, char *srcpath, char *dstpath)
{
int rval = ELAST + 1;
int fd = -1;
struct stat sb;
void *buf;
CFDataRef data = NULL;
CFMutableDictionaryRef pldict = NULL;
CFStringRef str = NULL;
mode_t dirmode;
char dstparent[PATH_MAX];
CFIndex len;
if (-1 == (fd = sopen(up->caches->cachefd, srcpath, O_RDONLY, 0)))
goto finish;
if (fstat(fd, &sb)) goto finish;
if (!(buf = malloc(sb.st_size))) goto finish;
if (read(fd, buf, sb.st_size) != sb.st_size) goto finish;
if (!(data = CFDataCreate(nil, buf, sb.st_size))) goto finish;
pldict = (CFMutableDictionaryRef)CFPropertyListCreateFromXMLData(nil, data,
kCFPropertyListMutableContainers, NULL );
if (!pldict || CFGetTypeID(pldict)!=CFDictionaryGetTypeID()) {
pldict = CFDictionaryCreateMutable(nil, 0 ,
&kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks);
if (!pldict) goto finish;
}
str = CFStringCreateWithFileSystemRepresentation(nil, up->caches->uuid_str);
str = CFStringCreateWithCString(nil, up->caches->uuid_str, kCFStringEncodingASCII);
if (!str) goto finish;
CFDictionarySetValue(pldict, CFSTR(kRootUUIDKey), str);
(void)sunlink(up->curbootfd, dstpath);
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;
close(fd);
if (-1 == (fd=sopen(up->curbootfd, dstpath, O_WRONLY|O_CREAT, sb.st_mode)))
goto finish;
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 (str) CFRelease(str);
if (data) CFRelease(data);
if (pldict) CFRelease(pldict);
if (fd != -1) close(fd);
return rval;
}
static int ucopyRPS(struct updatingVol *up)
{
int rval = ELAST+1;
char discard[PATH_MAX];
struct stat sb;
int i;
char srcpath[PATH_MAX], dstpath[PATH_MAX];
char * plistNamePtr;
if (g_verbose_level > 2) kextd_log("Beginning copy and atomic activation");
if (FindRPSDir(up, up->curRPS, discard, discard)) goto finish;
if (stat(up->curRPS, &sb) == 0) {
if (sdeepunlink(up->curbootfd, up->curRPS)) {
kextd_error_log("%s: %s", up->curRPS, strerror(errno));
goto finish;
}
}
if (smkdir(up->curbootfd, up->curRPS, kRPSDirMask)) {
kextd_error_log("%s: %s", up->curRPS, strerror(errno));
goto finish;
}
for (i = 0; i < up->caches->nrps; i++) {
pathcpy(srcpath, up->caches->root);
pathcat(srcpath, up->caches->rpspaths[i].rpath);
pathcpy(dstpath, up->curRPS);
pathcat(dstpath, up->caches->rpspaths[i].rpath);
if (&up->caches->rpspaths[i] == up->caches->bootconfig) {
COMPILE_TIME_ASSERT(sizeof(BOOTPLIST_NAME) == sizeof(BOOTPLIST_APM_NAME));
if (!up->isGPT) {
plistNamePtr = strstr(dstpath, BOOTPLIST_NAME);
if (plistNamePtr) {
strncpy(plistNamePtr, BOOTPLIST_APM_NAME, strlen(BOOTPLIST_NAME));
}
}
if (insertUUID(up, srcpath, dstpath)) {
kextd_error_log("error populating config file %s", dstpath);
continue;
}
} else {
if (scopyfile(up->caches->cachefd,srcpath,up->curbootfd,dstpath)) {
kextd_error_log("error copying %s", srcpath);
goto finish;
}
}
}
rval = 0;
finish:
return rval;
}
static int ucopyMisc(struct updatingVol *up)
{
int rval = -1;
int i, nprocessed = 0;
char srcpath[PATH_MAX], dstpath[PATH_MAX];
struct stat sb;
if (g_verbose_level > 2) kextd_log("Copying new non-booter files");
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 (scopyfile(up->caches->cachefd,srcpath,up->curbootfd,dstpath)) {
kextd_error_log("error copying %s to %s", srcpath, dstpath);
}
continue;
}
nprocessed++;
}
rval = (nprocessed != i);
finish:
return rval;
}
static int moveLabels(struct updatingVol *up)
{
int rval = 0;
char path[PATH_MAX];
struct stat sb;
int fd = -1;
if (g_verbose_level > 2) kextd_log("Moving aside old labels");
pathcpy(path, up->curMount);
pathcat(path, up->caches->label->rpath);
if (0 == (stat(path, &sb))) {
char newpath[PATH_MAX];
unsigned char tbxichrp[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,&tbxichrp,sizeof(tbxichrp),0,0))
goto finish;
}
up->changestate = noLabels;
finish:
if (fd != -1) close(fd);
return rval;
}
static int nukeLabels(struct updatingVol *up)
{
int rval = 0;
char labelp[PATH_MAX];
struct stat sb;
if (g_verbose_level > 2) kextd_log("Destroying old labels");
pathcpy(labelp, up->curMount);
pathcat(labelp, up->caches->label->rpath);
if (0 == (stat(labelp, &sb))) {
rval |= sunlink(up->curbootfd, labelp);
}
pathcat(labelp, CONTENTEXT);
if (0 == (stat(labelp, &sb))) {
rval |= sunlink(up->curbootfd, labelp);
}
up->changestate = noLabels;
finish:
return rval;
}
static int ucopyBooters(struct updatingVol *up)
{
int rval = ELAST + 1;
char srcpath[PATH_MAX], oldpath[PATH_MAX];
if (g_verbose_level > 2) kextd_log("Copying new booters");
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);
if (srename(up->curbootfd, up->ofdst, oldpath) && errno !=ENOENT)
goto finish;
if (scopyfile(up->caches->cachefd, srcpath, up->curbootfd, up->ofdst)) {
kextd_error_log("%s: %s", srcpath, strerror(errno));
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);
if (srename(up->curbootfd, up->efidst, oldpath) && errno != ENOENT)
goto finish;
if (scopyfile(up->caches->cachefd, srcpath, up->curbootfd, up->efidst)){
kextd_error_log("failure copying booter %s", srcpath);
goto finish;
}
}
up->changestate = copiedBooters;
rval = 0;
finish:
return rval;
}
#define CLOSE(fd) do { (void)close(fd); fd = -1; } while(0)
enum blessIndices {
kSystemFolderIdx = 0,
kEFIBooterIdx = 1
};
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];
if (g_verbose_level > 2) kextd_log("Activating new booters");
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);
vinfo[kSystemFolderIdx] = sb.st_ino;
}
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);
vinfo[kEFIBooterIdx] = sb.st_ino;
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);
vinfo[kSystemFolderIdx] = sb.st_ino;
}
}
if (schdir(up->curbootfd, up->curMount, &fd)) goto finish;
if ((rval = BLSetVolumeFinderInfo(NULL, ".", vinfo))) goto finish;
(void)restoredir(fd); fd = -1;
up->changestate = activatedBooters;
finish:
if (fd != -1) close(fd);
return rval;
}
static int activateRPS(struct updatingVol *up)
{
int rval = ELAST + 1;
char prevRPS[PATH_MAX], curRPS[PATH_MAX], nextRPS[PATH_MAX];
if (g_verbose_level > 2) kextd_log("Completing copy and atomic activation");
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:
return rval;
}
#ifndef OPENSOURCE // BLGenerateOFLabel uses CG
static int writeLabels(struct updatingVol *up, char *labelp)
{
int rval = ELAST + 1;
CFDataRef lData = NULL;
CFIndex len;
int fd = -1;
char bootname[NAME_MAX];
char contentPath[PATH_MAX];
char *fmt = (CFArrayGetCount(up->boots) == 1) ? "%s" : "%s %d";
if (g_verbose_level > 2) kextd_log("Writing new labels");
if (NAME_MAX <= snprintf(bootname, NAME_MAX, fmt,
up->caches->volname, up->bootIdx + 1))
goto finish;
if (BLGenerateOFLabel(NULL, bootname, &lData)) goto finish;
if (-1 == (fd = sopen(up->curbootfd, labelp, O_CREAT|O_WRONLY, 0644)))
goto finish;
len = CFDataGetLength(lData);
if (write(fd, CFDataGetBytePtr(lData), len) != len) goto finish;
pathcpy(contentPath, labelp);
pathcat(contentPath, CONTENTEXT);
close(fd);
if (-1 == (fd = sopen(up->curbootfd, contentPath, O_CREAT|O_WRONLY, 0644)))
goto finish;
len = strlen(bootname);
if (write(fd, bootname, len) != len) goto finish;
rval = 0;
finish:
if (fd != -1) close(fd);
if (lData) CFRelease(lData);
return rval;
}
#endif // OPENSOURCE
static int activateMisc(struct updatingVol *up) {
int rval = ELAST + 1;
char path[PATH_MAX], opath[PATH_MAX];
int 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) {
if (g_verbose_level > 2) kextd_log("Activating non-booter files");
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);
return rval;
}
static int nukeFallbacks(struct updatingVol *up)
{
int rval = 0; int bsderr;
char delpath[PATH_MAX];
struct bootCaches *caches = up->caches;
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 toss[PATH_MAX];
if (0 == FindRPSDir(up, delpath, toss, toss)) {
if ((bsderr=sdeepunlink(up->curbootfd,delpath)) && bsderr!=ENOENT) {
rval |= bsderr;
}
}
}
finish:
return rval;
}