#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mount.h>
#include <IOKit/IOBSD.h>
#include <DiskArbitration/DiskArbitration.h>
#include <CoreFoundation/CoreFoundation.h>
#include "enums.h"
#include "bless.h"
extern int blesscontextprintf(BLContextPtr context, int loglevel, char const *fmt, ...);
static int updateAppleBootIfPresent(BLContextPtr context, char *device, CFDataRef bootxData,
CFDataRef labelData);
int setboot(BLContextPtr context, char *device, CFDataRef bootxData,
CFDataRef labelData)
{
int err;
CFTypeRef bootData = NULL;
err = BLGetRAIDBootDataForDevice(context, device, &bootData);
if(err) {
blesscontextprintf(context, kBLLogLevelError, "Error while determining if %s is a RAID\n", device );
return 3;
}
if(bootData) {
err = BLUpdateRAIDBooters(context, device, bootData, bootxData, labelData);
if(err) {
blesscontextprintf(context, kBLLogLevelError, "Error while updating RAID booters for %s\n", device );
}
CFRelease(bootData);
} else {
err = updateAppleBootIfPresent(context, device, bootxData, labelData);
if(err) {
blesscontextprintf(context, kBLLogLevelError, "Error while updating booter for %s\n", device );
}
}
if(BLIsOpenFirmwarePresent(context)) {
err = BLSetOpenFirmwareBootDevice(context, device);
if(err) {
blesscontextprintf(context, kBLLogLevelError, "Can't set Open Firmware\n" );
return 1;
} else {
blesscontextprintf(context, kBLLogLevelVerbose, "Open Firmware set successfully\n" );
}
} else {
err = BLSetActiveBIOSBootDevice(context, device);
if(err) {
blesscontextprintf(context, kBLLogLevelError, "Can't set active boot partition as %s\n", device);
return 4;
} else {
blesscontextprintf(context, kBLLogLevelVerbose, "%s set as active boot partition\n" , device);
}
}
return 0;
}
static int updateAppleBootIfPresent(BLContextPtr context, char *device, CFDataRef bootxData,
CFDataRef labelData)
{
unsigned char booterDev[MAXPATHLEN];
io_service_t service = 0;
CFStringRef name = NULL;
int32_t needsBooter = 0;
int32_t isBooter = 0;
BLUpdateBooterFileSpec *spec = NULL;
int32_t specCount = 0, currentCount = 0;
int ret;
strcpy(booterDev, "/dev/");
ret = BLDeviceNeedsBooter(context, device,
&needsBooter,
&isBooter,
&service);
if(ret) {
blesscontextprintf(context, kBLLogLevelError, "Could not determine if partition needs booter\n" );
return 1;
}
if(!(needsBooter || isBooter))
return 0;
for(;;) {
DADiskRef disk = NULL;
DASessionRef session = NULL;
CFDictionaryRef props = NULL;
CFStringRef daName = NULL;
char label[MAXPATHLEN];
if(labelData) break;
session = DASessionCreate(kCFAllocatorDefault);
if(session == NULL) {
blesscontextprintf(context, kBLLogLevelVerbose, "Can't connect to DiskArb\n");
break;
}
disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, device+5);
if(disk == NULL) {
CFRelease(session);
blesscontextprintf(context, kBLLogLevelVerbose, "Can't create DADisk for %s\n",
device + 5);
break;
}
props = DADiskCopyDescription(disk);
if(props == NULL) {
CFRelease(session);
CFRelease(disk);
blesscontextprintf(context, kBLLogLevelVerbose, "Can't get properties for %s\n",
device + 5);
break;
}
daName = CFDictionaryGetValue(props, kDADiskDescriptionVolumeNameKey);
if(daName == NULL) {
CFRelease(props);
CFRelease(disk);
CFRelease(session);
blesscontextprintf(context, kBLLogLevelVerbose, "Can't get properties for %s\n",
device + 5);
break;
}
if(!CFStringGetCString(daName, label, sizeof(label),
kCFStringEncodingUTF8)) {
CFRelease(props);
CFRelease(disk);
CFRelease(session);
break;
}
CFRelease(props);
CFRelease(disk);
CFRelease(session);
ret = BLGenerateOFLabel(context, label, &labelData);
if(ret)
labelData = NULL;
break;
}
if(!(bootxData || labelData)) {
IOObjectRelease(service);
return 0;
}
name = IORegistryEntryCreateCFProperty( service, CFSTR(kIOBSDNameKey),
kCFAllocatorDefault, 0);
if(name == NULL || CFStringGetTypeID() != CFGetTypeID(name)) {
IOObjectRelease(service);
blesscontextprintf(context, kBLLogLevelError, "Could not find bsd name for %x\n" , service);
return 2;
}
IOObjectRelease(service); service = 0;
if(!CFStringGetCString(name,booterDev+5,sizeof(booterDev)-5,kCFStringEncodingUTF8)) {
CFRelease(name);
blesscontextprintf(context, kBLLogLevelError, "Could not find bsd name for %x\n" , service);
return 3;
}
CFRelease(name);
if(labelData) specCount += 2;
if(bootxData) specCount += 1;
spec = calloc(specCount, sizeof(spec[0]));
if(labelData) {
spec[currentCount+0].version = 0;
spec[currentCount+0].reqType = kBL_OSTYPE_PPC_TYPE_OFLABEL;
spec[currentCount+0].reqCreator = kBL_OSTYPE_PPC_CREATOR_CHRP;
spec[currentCount+0].reqFilename = NULL;
spec[currentCount+0].payloadData = labelData;
spec[currentCount+0].postType = 0; spec[currentCount+0].postCreator = 0; spec[currentCount+0].foundFile = 0;
spec[currentCount+0].updatedFile = 0;
spec[currentCount+1].version = 0;
spec[currentCount+1].reqType = kBL_OSTYPE_PPC_TYPE_OFLABEL_PLACEHOLDER;
spec[currentCount+1].reqCreator = kBL_OSTYPE_PPC_CREATOR_CHRP;
spec[currentCount+1].reqFilename = NULL;
spec[currentCount+1].payloadData = labelData;
spec[currentCount+1].postType = kBL_OSTYPE_PPC_TYPE_OFLABEL;
spec[currentCount+1].postCreator = 0; spec[currentCount+1].foundFile = 0;
spec[currentCount+1].updatedFile = 0;
currentCount += 2;
}
if(bootxData) {
spec[currentCount+0].version = 0;
spec[currentCount+0].reqType = kBL_OSTYPE_PPC_TYPE_BOOTX;
spec[currentCount+0].reqCreator = kBL_OSTYPE_PPC_CREATOR_CHRP;
spec[currentCount+0].reqFilename = NULL;
spec[currentCount+0].payloadData = bootxData;
spec[currentCount+0].postType = 0; spec[currentCount+0].postCreator = 0; spec[currentCount+0].foundFile = 0;
spec[currentCount+0].updatedFile = 0;
}
ret = BLUpdateBooter(context, booterDev, spec, specCount);
if(ret) {
blesscontextprintf(context, kBLLogLevelError, "Error enumerating HFS+ volume\n");
return 1;
}
if(bootxData) {
if(!(spec[currentCount].foundFile)) {
blesscontextprintf(context, kBLLogLevelError, "No pre-existing BootX found in HFS+ volume\n");
return 2;
}
if(!(spec[currentCount].updatedFile)) {
blesscontextprintf(context, kBLLogLevelError, "BootX was not updated\n");
return 3;
}
}
if(labelData) {
if(!(spec[0].foundFile || spec[1].foundFile)) {
blesscontextprintf(context, kBLLogLevelError, "No pre-existing OF label found in HFS+ volume\n");
return 2;
}
if(!(spec[0].updatedFile || spec[1].updatedFile)) {
blesscontextprintf(context, kBLLogLevelError, "OF label was not updated\n");
return 3;
}
}
free(spec);
return 0;
}