#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> // DEVMAXPATHSIZE
#include <CoreFoundation/CoreFoundation.h>
#include <DiskArbitration/DiskArbitration.h>
#include "bootroot.h" // eventually "bootcaches.h" (v2)
#include "bootfiles.h"
#include "logging.h"
#include "safecalls.h"
#include "update_boot.h"
enum bootReversions {
nothingSerious = 0,
nukedLabels, copyingOFBooter, copyingEFIBooter, copiedBooters, activatingOFBooter, activatingEFIBooter, activatedBooters };
#define OLDEXT ".old"
#define NEWEXT ".new"
#define CONTENTEXT ".contentDetails"
struct updatingVol {
int curbootfd; char curMount[MNAMELEN]; DADiskRef curBoot; char curRPS[PATH_MAX]; char efidst[PATH_MAX], ofdst[PATH_MAX];
enum bootReversions changestate;
Boolean doRPS, doMisc, doBooters; CFArrayRef boots; DASessionRef dasession; struct bootCaches *caches; };
static int mountBoot(struct updatingVol *up, CFIndex bootindex);
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 nukeLabels(struct updatingVol *s); static int activateBooters(struct updatingVol *s); static int activateRPS(struct updatingVol *s); static int activateMisc(struct updatingVol *s, int bidx); 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)
int updateBoots(char *volRoot, int filec, const char *files[],
Boolean force, int dashv)
{
int rval;
char *errmsg = NULL;
struct updatingVol up = { -1, { '\0' }, };
char bsdname[DEVMAXPATHSIZE];
CFDictionaryRef bdict = NULL;
struct stat cachesb;
CFIndex i, bootcount, bootupdates = 0;
Boolean doAny;
rval = 0;
if (takeVolumeForPaths(volRoot, filec, files)) goto finish; up.caches = readCaches(volRoot);
if (!up.caches) goto finish;
rval = ELAST + 1;
errmsg = "couldn't rebuild stale mkext?"; if (check_mkext(up.caches)) {
putVolumeForPath(volRoot, EX_TEMPFAIL);
if (rebuild_mkext(up.caches, true )) goto finish;
errmsg = NULL; if (takeVolumeForPaths(volRoot, filec, files)) goto finish;
}
errmsg = "couldn't get Apple_Boot information";
if (fstat(up.caches->cachefd, &cachesb)) goto finish; if (!(devname_r(cachesb.st_dev,S_IFBLK,bsdname,DEVMAXPATHSIZE)))goto finish;
if (BLCreateBooterInformationDictionary(NULL, bsdname, &bdict))
goto finish;
if (fstat(up.caches->cachefd, &cachesb)) goto finish;
up.boots = CFDictionaryGetValue(bdict, kBLAuxiliaryPartitionsKey);
if (!up.boots) goto finish; bootcount = CFArrayGetCount(up.boots);
if (!bootcount) {
rval = 0; if (dashv > 0) kextd_log("no helper partitions; skipping update");
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 (dashv > 0) kextd_log("helper partitions appear up to date");
goto finish;
}
if (force) up.doRPS = up.doBooters = up.doMisc = true;
errmsg = "trouble setting up DiskArb";
if (!(up.dasession = DASessionCreate(nil))) goto finish;
DASessionScheduleWithRunLoop(up.dasession, CFRunLoopGetCurrent(),
kCFRunLoopDefaultMode);
errmsg = "trouble updating one or more helper partitions";
for (i = 0; i < bootcount; i++) {
up.changestate = nothingSerious; if ((mountBoot(&up, i))) goto bootfail; if (up.doRPS && ucopyRPS(&up)) goto bootfail; if (up.doMisc) (void) ucopyMisc(&up);
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, i)) goto bootfail;
up.changestate = nothingSerious;
bootupdates++; if (dashv > 1) {
kextd_log("successfully updated helper partition #%d", i);
}
bootfail:
if (dashv > 0 && up.changestate != nothingSerious) {
kextd_error_log("error updating helper partition #%d, state %d", i,
up.changestate);
}
(void)revertState(&up);
if (nukeFallbacks(&up)) kextd_error_log("helper #%d may be untidy", i);
if (unmountBoot(&up)) kextd_error_log("unmount trouble??");
}
if (bootupdates != bootcount) goto finish;
errmsg = "trouble updating bootstamps";
if (applyStamps(up.caches)) goto finish;
rval = 0;
finish:
putVolumeForPath(volRoot, rval);
if (bdict) CFRelease(bdict);
if (up.curbootfd != -1) close(up.curbootfd);
if (up.dasession) {
DASessionUnscheduleFromRunLoop(up.dasession, CFRunLoopGetCurrent(),
kCFRunLoopDefaultMode);
CFRelease(up.dasession);
}
if (rval && 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;
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 nukedLabels:
doMisc = up->doMisc;
up->doMisc = false;
rval |= activateMisc(up, 0); up->doMisc = doMisc;
case nothingSerious:
break;
}
finish:
return rval;
};
static int mountBoot(struct updatingVol *up, CFIndex bidx)
{
int rval = ELAST + 1;
char bsdname[DEVMAXPATHSIZE];
CFStringRef mountargs[] = { CFSTR("perm"), CFSTR("nobrowse"), NULL };
CFStringRef str;
DADissenterRef dis = (void*)kCFNull;
CFDictionaryRef ddesc = NULL;
CFURLRef volURL;
struct statfs bsfs;
struct stat secsb;
str = (CFStringRef)CFArrayGetValueAtIndex(up->boots, bidx);
if (!str) goto finish;
if (!CFStringGetFileSystemRepresentation(str, bsdname, DEVMAXPATHSIZE))
goto finish;
if (!(up->curBoot = DADiskCreateFromBSDName(nil, up->dasession, bsdname)))
goto finish;
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, bidx);
}
}
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 < 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) {
kextd_error_log("couldn't mount helper: error %X (DA: %d)", rval,
rval & ~(err_local|err_local_diskarbitration));
}
return rval;
}
static int unmountBoot(struct updatingVol *up)
{
int rval = ELAST + 1;
DADissenterRef dis = (void*)kCFNull;
if (!up->curBoot) goto finish;
if (!up->curMount[0]) goto finish;
if (up->curbootfd != -1) close(up->curbootfd);
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);
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) { printf("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:
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;
CFIndex len;
mode_t dirmode;
char dstparent[PATH_MAX];
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;
}
CFDictionarySetValue(pldict, CFSTR(kRootUUIDKey), up->caches->volUUIDStr);
(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 (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];
if (FindRPSDir(up, up->curRPS, discard, discard)) goto finish;
if (stat(up->curRPS, &sb) == 0) {
if (sdeepunlink(up->curbootfd, up->curRPS)) goto finish;
}
if (smkdir(up->curbootfd, up->curRPS, kRPSDirMask)) 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) {
if (insertUUID(up, srcpath, dstpath)) {
kextd_error_log("error populating config file %s", dstpath);
continue;
}
} else {
if (stat(srcpath, &sb) == 0 && sb.st_size == 0) {
kextd_error_log("zero-size RPS file %s?", srcpath);
goto finish;
}
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;
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 nukeLabels(struct updatingVol *up)
{
int rval = 0;
char labelp[PATH_MAX];
struct stat sb;
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 = nukedLabels;
finish:
return rval;
}
static int ucopyBooters(struct updatingVol *up)
{
int rval = ELAST + 1;
char srcpath[PATH_MAX], oldpath[PATH_MAX];
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)) goto finish;
if (scopyfile(up->caches->cachefd, srcpath, up->curbootfd, up->ofdst)) {
kextd_error_log("failure copying booter %s", srcpath);
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];
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)); goto finish;
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 (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 bidx)
{
int rval = ELAST + 1;
CFDataRef lData = NULL;
CFIndex len;
int fd = -1;
char bootname[NAME_MAX];
char contentPath[PATH_MAX];
if (NAME_MAX <= snprintf(bootname, NAME_MAX, "%s %d",
up->caches->volname, bidx + 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(up->caches->volname);
if (write(fd, up->caches->volname, 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 bidx) {
int rval = ELAST + 1;
char labelp[PATH_MAX], 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) {
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(labelp, up->curMount);
pathcat(labelp, up->caches->label->rpath);
#ifndef OPENSOURCE
(void)sunlink(up->curbootfd, labelp);
if (writeLabels(up, labelp, bidx)) goto finish;
#endif
if (0 == (stat(labelp, &sb))) {
if (-1 == (fd = sopen(up->curbootfd, labelp, O_RDWR, 0))) goto finish;
if (fsetxattr(fd,XATTR_FINDERINFO_NAME,&tbxjchrp,sizeof(tbxjchrp),0,0))
goto finish;
}
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;
}