AppleRAIDUserLib.c [plain text]
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/param.h>
#include <mach/mach.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFSerialize.h>
#include <IOKit/IOCFUnserialize.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOBSD.h>
#include <IOKit/storage/IOMedia.h>
#include "AppleRAIDUserClient.h"
#include "AppleRAIDUserLib.h"
#include "AppleRAIDMember.h"
#include "AppleLVMGroup.h"
#include "AppleLVMVolume.h"
#include "AppleRAIDConcatSet.h"
#include "AppleRAIDMirrorSet.h"
#include "AppleRAIDStripeSet.h"
#ifdef DEBUG
#define IOLog1(...) { printf(__VA_ARGS__); fflush(stdout); }
#endif DEBUG
#ifndef IOLog1
#define IOLog1(args...)
#endif
#ifndef IOLog2
#define IOLog2(args...)
#endif
static io_connect_t gRAIDControllerPort = 0;
static io_connect_t
AppleRAIDOpenConnection()
{
kern_return_t kr;
CFDictionaryRef classToMatch;
io_service_t serviceObject;
if (gRAIDControllerPort) return gRAIDControllerPort;
classToMatch = IOServiceMatching(kAppleRAIDUserClassName);
if (classToMatch == NULL)
{
IOLog1("IOServiceMatching returned a NULL dictionary.\n");
return kIOReturnNoResources;
}
serviceObject = IOServiceGetMatchingService(kIOMasterPortDefault, classToMatch);
if (!serviceObject)
{
IOLog2("Couldn't find any matches.\n");
return kIOReturnNoResources;
}
kr = IOServiceOpen(serviceObject, mach_task_self(), 0, &gRAIDControllerPort);
IOObjectRelease(serviceObject);
if (kr != KERN_SUCCESS)
{
IOLog1("IOServiceOpen returned %d\n", kr);
return kr;
}
kr = IOConnectCallStructMethod(gRAIDControllerPort, kAppleRAIDClientOpen, 0, 0, 0, 0);
UInt32 count = 0;
while (kr == kIOReturnExclusiveAccess && count < 60)
{
#ifdef DEBUG
if ((count % 15) == 0) IOLog1("AppleRAID: controller object is busy, retrying...\n");
#endif
(void)sleep(1);
kr = IOConnectCallStructMethod(gRAIDControllerPort, kAppleRAIDClientOpen, 0, 0, 0, 0);
count++;
}
if (kr != KERN_SUCCESS)
{
printf("AppleRAID: failed trying to get controller object, rc = 0x%x.\n", kr);
IOServiceClose(gRAIDControllerPort);
gRAIDControllerPort = 0;
}
return gRAIDControllerPort;
}
static kern_return_t
AppleRAIDCloseConnection()
{
kern_return_t kr;
if (!gRAIDControllerPort) return kIOReturnSuccess;
kr = IOConnectCallStructMethod(gRAIDControllerPort, kAppleRAIDClientClose, 0, 0, 0, 0);
if (kr != KERN_SUCCESS)
{
IOLog1("AppleRAIDClientClose returned %d\n", kr);
return kr;
}
kr = IOServiceClose(gRAIDControllerPort);
if (kr != KERN_SUCCESS)
{
IOLog1("IOServiceClose returned %d\n", kr);
return kr;
}
gRAIDControllerPort = 0;
return kr;
}
typedef struct changeInfo {
io_object_t service;
mach_port_t notifier;
CFStringRef uuidString;
} changeInfo_t;
static IONotificationPortRef gNotifyPort;
static io_iterator_t gRAIDSetIter;
static io_iterator_t gLogicalVolumeIter;
static void
raidSetChanged(void *refcon, io_service_t service, natural_t messageType, void *messageArgument)
{
changeInfo_t * changeInfo = (changeInfo_t *)refcon;
if (messageType == kIOMessageServiceIsTerminated) {
CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(),
CFSTR(kAppleRAIDNotificationSetTerminated),
changeInfo->uuidString,
NULL, false);
IOObjectRelease(changeInfo->service);
IOObjectRelease(changeInfo->notifier);
CFRelease(changeInfo->uuidString);
free(changeInfo);
return;
}
IOLog2("raidSetChanged: messageType %08x, arg %08lx\n", messageType, (UInt32) messageArgument);
if (messageType != kAppleRAIDMessageSetChanged) return;
CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(),
CFSTR(kAppleRAIDNotificationSetChanged),
changeInfo->uuidString,
NULL, false);
}
void static
raidSetDetected(void *refCon, io_iterator_t iterator)
{
kern_return_t kr;
io_service_t newSet;
changeInfo_t * changeInfo;
CFMutableDictionaryRef registryEntry;
CFStringRef uuidString;
while (newSet = IOIteratorNext(iterator)) {
kr = IORegistryEntryCreateCFProperties(newSet, ®istryEntry, kCFAllocatorDefault, 0);
if (kr != KERN_SUCCESS) return;
uuidString = CFDictionaryGetValue(registryEntry, CFSTR(kAppleRAIDMemberUUIDKey));
if (uuidString) uuidString = CFStringCreateCopy(NULL, uuidString);
CFRelease(registryEntry);
if (!uuidString) return;
changeInfo = calloc(1, sizeof(changeInfo_t));
changeInfo->service = newSet;
changeInfo->uuidString = uuidString;
kr = IOServiceAddInterestNotification(gNotifyPort, newSet, kIOGeneralInterest,
&raidSetChanged, (void *)changeInfo,
&changeInfo->notifier);
if (kr != KERN_SUCCESS) {
free(changeInfo);
return;
}
CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(),
CFSTR(kAppleRAIDNotificationSetDiscovered),
uuidString,
NULL, false);
}
}
static void
logicalVolumeChanged(void *refcon, io_service_t service, natural_t messageType, void *messageArgument)
{
changeInfo_t * changeInfo = (changeInfo_t *)refcon;
if (messageType == kIOMessageServiceIsTerminated) {
CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(),
CFSTR(kAppleLVMNotificationVolumeTerminated),
changeInfo->uuidString,
NULL, false);
IOObjectRelease(changeInfo->service);
IOObjectRelease(changeInfo->notifier);
CFRelease(changeInfo->uuidString);
free(changeInfo);
return;
}
IOLog2("logicalVolumeChanged: messageType %08x, arg %08lx\n", messageType, (UInt32) messageArgument);
if (messageType != kAppleLVMMessageVolumeChanged) return;
CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(),
CFSTR(kAppleLVMNotificationVolumeChanged),
changeInfo->uuidString,
NULL, false);
}
void static
logicalVolumeDetected(void *refCon, io_iterator_t iterator)
{
kern_return_t kr;
io_service_t newVolume;
changeInfo_t * changeInfo;
CFMutableDictionaryRef registryEntry;
CFStringRef uuidString;
while (newVolume = IOIteratorNext(iterator)) {
kr = IORegistryEntryCreateCFProperties(newVolume, ®istryEntry, kCFAllocatorDefault, 0);
if (kr != KERN_SUCCESS) return;
uuidString = CFDictionaryGetValue(registryEntry, CFSTR("UUID"));
if (uuidString) uuidString = CFStringCreateCopy(NULL, uuidString);
CFRelease(registryEntry);
if (!uuidString) return;
changeInfo = calloc(1, sizeof(changeInfo_t));
changeInfo->service = newVolume;
changeInfo->uuidString = uuidString;
kr = IOServiceAddInterestNotification(gNotifyPort, newVolume, kIOGeneralInterest,
&logicalVolumeChanged, (void *)changeInfo,
&changeInfo->notifier);
if (kr != KERN_SUCCESS) {
free(changeInfo);
return;
}
CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(),
CFSTR(kAppleLVMNotificationVolumeDiscovered),
uuidString,
NULL, false);
}
}
kern_return_t
AppleRAIDEnableNotifications()
{
kern_return_t kr;
CFDictionaryRef classToMatch;
CFRunLoopSourceRef runLoopSource;
IOLog1("AppleRAIDEnableNotifications entered\n");
gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault);
runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
classToMatch = IOServiceMatching(kAppleRAIDSetClassName);
if (classToMatch == NULL)
{
IOLog1("IOServiceMatching returned a NULL dictionary.\n");
return kIOReturnNoResources;
}
kr = IOServiceAddMatchingNotification( gNotifyPort,
kIOFirstMatchNotification,
classToMatch,
raidSetDetected,
NULL,
&gRAIDSetIter );
if (kr != KERN_SUCCESS)
{
IOLog1("IOServiceAddMatchingNotification returned %d\n", kr);
return kr;
}
raidSetDetected(NULL, gRAIDSetIter);
classToMatch = IOServiceMatching(kAppleLogicalVolumeClassName);
if (classToMatch == NULL)
{
IOLog1("IOServiceMatching returned a NULL dictionary.\n");
return kIOReturnNoResources;
}
kr = IOServiceAddMatchingNotification( gNotifyPort,
kIOFirstMatchNotification,
classToMatch,
logicalVolumeDetected,
NULL,
&gLogicalVolumeIter );
if (kr != KERN_SUCCESS)
{
IOLog1("IOServiceAddMatchingNotification returned %d\n", kr);
return kr;
}
logicalVolumeDetected(NULL, gLogicalVolumeIter); return kr;
}
kern_return_t
AppleRAIDDisableNotifications(void)
{
IONotificationPortDestroy(gNotifyPort);
if (gRAIDSetIter) {
IOObjectRelease(gRAIDSetIter);
gRAIDSetIter = 0;
}
if (gLogicalVolumeIter) {
IOObjectRelease(gLogicalVolumeIter);
gLogicalVolumeIter = 0;
}
return KERN_SUCCESS;
}
typedef struct memberInfo {
CFStringRef diskNameCF;
io_name_t diskName;
io_name_t wholeDiskName;
unsigned int partitionNumber;
char devicePath[256];
io_name_t regName;
UInt64 size;
UInt64 blockSize;
bool isWhole;
bool isRAID;
CFStringRef uuidString;
UInt64 headerOffset;
UInt64 chunkCount;
UInt64 chunkSize;
UInt64 primaryMetaDataSize;
UInt64 secondaryMetaDataSize;
UInt64 startOffset;
AppleRAIDPrimaryOnDisk * primaryData;
void * secondaryData;
} memberInfo_t;
static void
freeMemberInfo(memberInfo_t * m)
{
if (m->diskNameCF) CFRelease(m->diskNameCF);
if (m->uuidString) CFRelease(m->uuidString);
if (m->primaryData) free(m->primaryData);
if (m->secondaryData) free(m->secondaryData);
free(m);
}
static memberInfo_t *
getMemberInfo(CFStringRef partitionName)
{
CFIndex diskNameSize = CFStringGetLength(partitionName);
diskNameSize = CFStringGetMaximumSizeForEncoding(diskNameSize, kCFStringEncodingUTF8) + 1;
char *diskName = malloc(diskNameSize);
if (!CFStringGetCString(partitionName, diskName, diskNameSize, kCFStringEncodingUTF8)) return NULL;
io_registry_entry_t obj = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, diskName));
if (!obj){
IOLog1("AppleRAIDLib - getMemberInfo: IOServiceGetMatchingService failed for %s\n", diskName);
return NULL;
}
memberInfo_t * mi = calloc(1, sizeof(memberInfo_t));
mi->diskNameCF = partitionName;
CFRetain(partitionName);
strlcpy(mi->diskName, diskName, sizeof(io_name_t));
snprintf(mi->devicePath, sizeof(mi->devicePath), "/dev/%s", diskName);
IORegistryEntryGetName(obj, mi->regName);
CFMutableDictionaryRef properties = NULL;
IOReturn result = IORegistryEntryCreateCFProperties(obj, &properties, kCFAllocatorDefault, kNilOptions);
if (!result && properties) {
CFNumberRef number;
number = (CFNumberRef)CFDictionaryGetValue(properties, CFSTR(kIOMediaSizeKey));
if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &mi->size);
mi->headerOffset = ARHEADER_OFFSET(mi->size);
number = (CFNumberRef)CFDictionaryGetValue(properties, CFSTR(kIOMediaPreferredBlockSizeKey));
if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &mi->blockSize);
mi->isWhole = (CFBooleanRef)CFDictionaryGetValue(properties, CFSTR(kIOMediaWholeKey)) == kCFBooleanTrue;
mi->isRAID = (CFBooleanRef)CFDictionaryGetValue(properties, CFSTR(kAppleRAIDIsRAIDKey)) == kCFBooleanTrue;
mi->uuidString = (CFStringRef)CFDictionaryGetValue(properties, CFSTR(kIOMediaUUIDKey));
if (mi->uuidString) CFRetain(mi->uuidString);
strcpy(mi->wholeDiskName, mi->diskName);
if (!mi->isWhole) {
char * c = mi->wholeDiskName + 4; while (*c != 's' && *c++); if (*c == 's') {
*c = 0; sscanf(c+1, "%u", &mi->partitionNumber); }
}
} else {
freeMemberInfo(mi);
return NULL;
}
return mi;
}
typedef struct setInfo {
CFMutableDictionaryRef setProps;
CFIndex memberCount;
CFMutableArrayRef members;
CFMutableDictionaryRef * memberProps;
memberInfo_t ** memberInfo;
} setInfo_t;
static void
freeSetInfo(setInfo_t *setInfo)
{
CFIndex i;
if (setInfo->memberProps) {
for (i=0; i < setInfo->memberCount; i++) {
if (setInfo->memberProps[i]) CFRelease(setInfo->memberProps[i]);
}
free(setInfo->memberProps);
}
if (setInfo->memberInfo) {
for (i=0; i < setInfo->memberCount; i++) {
if (setInfo->memberInfo[i]) freeMemberInfo(setInfo->memberInfo[i]);
}
free(setInfo->memberInfo);
}
if (setInfo->setProps) CFRelease(setInfo->setProps);
free(setInfo);
}
static setInfo_t *
getSetInfo(AppleRAIDSetRef setRef)
{
setInfo_t * setInfo = calloc(1, sizeof(setInfo_t));
if (!setInfo) return NULL;
setInfo->setProps = AppleRAIDGetSetProperties(setRef);
if (!setInfo->setProps) goto error;
setInfo->members = (CFMutableArrayRef)CFDictionaryGetValue(setInfo->setProps, CFSTR(kAppleRAIDMembersKey));
setInfo->memberCount = setInfo->members ? CFArrayGetCount(setInfo->members) : 0;
if (setInfo->memberCount) {
setInfo->memberInfo = calloc(setInfo->memberCount, sizeof(memberInfo_t *));
setInfo->memberProps = calloc(setInfo->memberCount, sizeof(CFMutableDictionaryRef));
}
CFIndex i;
for (i=0; i < setInfo->memberCount; i++) {
CFStringRef member = (CFStringRef)CFArrayGetValueAtIndex(setInfo->members, i);
if (member) {
setInfo->memberProps[i] = AppleRAIDGetMemberProperties(member);
if (setInfo->memberProps[i]) {
CFStringRef partitionName = (CFStringRef)CFDictionaryGetValue(setInfo->memberProps[i], CFSTR(kIOBSDNameKey));
if (partitionName) {
setInfo->memberInfo[i] = getMemberInfo(partitionName);
}
CFMutableDictionaryRef props = setInfo->memberProps[i];
memberInfo_t * info = setInfo->memberInfo[i];
CFNumberRef number;
number = (CFNumberRef)CFDictionaryGetValue(setInfo->setProps, CFSTR(kAppleRAIDChunkSizeKey)); if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &info->chunkSize);
number = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kAppleRAIDChunkCountKey)); if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &info->chunkCount);
number = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kAppleRAIDPrimaryMetaDataUsedKey)); if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &info->primaryMetaDataSize);
number = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kAppleRAIDSecondaryMetaDataSizeKey)); if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &info->secondaryMetaDataSize);
number = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kAppleRAIDMemberStartKey)); if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &info->startOffset);
}
}
}
return setInfo;
error:
freeSetInfo(setInfo);
return NULL;
}
#define kMaxIOConnectTransferSize 4096
CFMutableArrayRef
AppleRAIDGetListOfSets(UInt32 filter)
{
kern_return_t kr;
size_t listSize = kMaxIOConnectTransferSize;
CFMutableArrayRef theList = NULL;
char * listString = (char *)malloc((int)listSize);
io_connect_t raidControllerPort = AppleRAIDOpenConnection();
if (!raidControllerPort) return NULL;
kr = IOConnectCallStructMethod(raidControllerPort, kAppleRAIDGetListOfSets, &filter, sizeof(filter), listString, &listSize); if (kr == KERN_SUCCESS) {
IOLog2("AppleRAIDGetListOfSets was successful.\n");
IOLog2("size = %d, theList = %s\n", (int)listSize, (char *)listString);
theList = (CFMutableArrayRef)IOCFUnserialize(listString, kCFAllocatorDefault, 0, NULL);
}
free(listString);
AppleRAIDCloseConnection();
return theList;
}
CFMutableDictionaryRef
AppleRAIDGetSetProperties(AppleRAIDSetRef setName)
{
kern_return_t kr;
size_t propSize = kMaxIOConnectTransferSize;
CFMutableDictionaryRef props = NULL;
size_t bufferSize = kAppleRAIDMaxUUIDStringSize;
char buffer[bufferSize];
if (!CFStringGetCString(setName, buffer, bufferSize, kCFStringEncodingUTF8)) {
IOLog1("AppleRAIDGetSetProperties() CFStringGetCString failed?\n");
return NULL;
}
io_connect_t raidControllerPort = AppleRAIDOpenConnection();
if (!raidControllerPort) return NULL;
char * propString = (char *)malloc(propSize);
kr = IOConnectCallStructMethod(raidControllerPort, kAppleRAIDGetSetProperties, buffer, bufferSize, propString, &propSize);
if (kr == KERN_SUCCESS) {
IOLog2("AppleRAIDGetSetProperties was successful.\n");
IOLog2("size = %d, prop = %s\n", (int)propSize, (char *)propString);
props = (CFMutableDictionaryRef)IOCFUnserialize(propString, kCFAllocatorDefault, 0, NULL);
}
free(propString);
AppleRAIDCloseConnection();
return props;
}
CFMutableDictionaryRef
AppleRAIDGetMemberProperties(AppleRAIDMemberRef memberName)
{
kern_return_t kr;
size_t propSize = kMaxIOConnectTransferSize;
CFMutableDictionaryRef props = NULL;
size_t bufferSize = kAppleRAIDMaxUUIDStringSize;
char buffer[bufferSize];
if (!CFStringGetCString(memberName, buffer, bufferSize, kCFStringEncodingUTF8)) {
IOLog1("AppleRAIDGetMemberProperties() CFStringGetCString failed?\n");
return NULL;
}
io_connect_t raidControllerPort = AppleRAIDOpenConnection();
if (!raidControllerPort) return NULL;
char * propString = (char *)malloc(propSize);
kr = IOConnectCallStructMethod(raidControllerPort, kAppleRAIDGetMemberProperties, buffer, bufferSize, propString, &propSize);
if (kr == KERN_SUCCESS) {
IOLog2("AppleRAIDGetMemberProperties was successful.\n");
IOLog2("size = %d, prop = %s\n", (int)propSize, (char *)propString);
props = (CFMutableDictionaryRef)IOCFUnserialize(propString, kCFAllocatorDefault, 0, NULL);
}
free(propString);
AppleRAIDCloseConnection();
return props;
}
static const char *raidDescriptionBuffer =
" <array> \n"
"<dict> \n"
"<key>" kAppleRAIDLevelNameKey "</key>" "<string>" kAppleRAIDLevelNameStripe "</string> \n"
"<key>" kAppleRAIDMemberTypeKey "</key>" "<array> \n"
"<string>" kAppleRAIDMembersKey "</string> \n"
"</array> \n"
"<key>" kAppleRAIDSetAutoRebuildKey "</key>" "<false/> \n"
"<key>" kAppleRAIDSetQuickRebuildKey "</key>" "<false/> \n"
"<key>" kAppleRAIDSetTimeoutKey "</key>" "<integer size=\"32\">0</integer> \n"
"<key>" kAppleRAIDChunkSizeKey "</key>" "<integer size=\"64\">0x8000</integer> \n"
"<key>" kAppleRAIDCanAddMembersKey "</key>" "<false/> \n"
"<key>" kAppleRAIDCanAddSparesKey "</key>" "<false/> \n"
"<key>" kAppleRAIDSizesCanVaryKey "</key>" "<false/> \n"
"<key>" kAppleRAIDRemovalAllowedKey "</key>" "<string>" kAppleRAIDRemovalNone "</string> \n"
"<key>" kAppleRAIDCanBeConvertedToKey "</key>" "<false/> \n"
"</dict> \n"
"<dict> \n"
"<key>" kAppleRAIDLevelNameKey "</key>" "<string>" kAppleRAIDLevelNameMirror "</string> \n"
"<key>" kAppleRAIDMemberTypeKey "</key>" "<array> \n"
"<string>" kAppleRAIDMembersKey "</string> \n"
"<string>" kAppleRAIDSparesKey "</string> \n"
"</array> \n"
"<key>" kAppleRAIDSetAutoRebuildKey "</key>" "<true/> \n"
"<key>" kAppleRAIDSetQuickRebuildKey "</key>" "<true/> \n"
"<key>" kAppleRAIDSetTimeoutKey "</key>" "<integer size=\"32\">30</integer> \n"
"<key>" kAppleRAIDChunkSizeKey "</key>" "<integer size=\"64\">0x8000</integer> \n"
"<key>" kAppleRAIDCanAddMembersKey "</key>" "<true/> \n"
"<key>" kAppleRAIDCanAddSparesKey "</key>" "<true/> \n"
"<key>" kAppleRAIDSizesCanVaryKey "</key>" "<false/> \n"
"<key>" kAppleRAIDRemovalAllowedKey "</key>" "<string>" kAppleRAIDRemovalAnyMember "</string> \n"
"<key>" kAppleRAIDCanBeConvertedToKey "</key>" "<true/> \n"
"</dict> \n"
"<dict> \n"
"<key>" kAppleRAIDLevelNameKey "</key>" "<string>" kAppleRAIDLevelNameConcat "</string> \n"
"<key>" kAppleRAIDMemberTypeKey "</key>" "<array> \n"
"<string>" kAppleRAIDMembersKey "</string> \n"
"</array> \n"
"<key>" kAppleRAIDSetAutoRebuildKey "</key>" "<false/> \n"
"<key>" kAppleRAIDSetQuickRebuildKey "</key>" "<false/> \n"
"<key>" kAppleRAIDSetTimeoutKey "</key>" "<integer size=\"32\">0</integer> \n"
"<key>" kAppleRAIDChunkSizeKey "</key>" "<integer size=\"64\">0x8000</integer> \n"
"<key>" kAppleRAIDCanAddMembersKey "</key>" "<true/> \n"
"<key>" kAppleRAIDCanAddSparesKey "</key>" "<false/> \n"
"<key>" kAppleRAIDSizesCanVaryKey "</key>" "<true/> \n"
"<key>" kAppleRAIDRemovalAllowedKey "</key>" "<string>" kAppleRAIDRemovalLastMember "</string> \n"
"<key>" kAppleRAIDCanBeConvertedToKey "</key>" "<true/> \n"
"</dict> \n"
"<dict> \n"
"<key>" kAppleRAIDLevelNameKey "</key>" "<string>" kAppleRAIDLevelNameLVG "</string> \n"
"<key>" kAppleRAIDMemberTypeKey "</key>" "<array> \n"
"<string>" kAppleRAIDMembersKey "</string> \n"
"</array> \n"
"<key>" kAppleRAIDSetAutoRebuildKey "</key>" "<false/> \n"
"<key>" kAppleRAIDSetTimeoutKey "</key>" "<integer size=\"32\">0</integer> \n"
"<key>" kAppleRAIDChunkSizeKey "</key>" "<integer size=\"64\">0x8000</integer> \n"
"<key>" kAppleRAIDCanAddMembersKey "</key>" "<true/> \n"
"<key>" kAppleRAIDCanAddSparesKey "</key>" "<false/> \n"
"<key>" kAppleRAIDSizesCanVaryKey "</key>" "<true/> \n"
"<key>" kAppleRAIDRemovalAllowedKey "</key>" "<string>" kAppleRAIDRemovalNone "</string> \n"
"<key>" kAppleRAIDCanBeConvertedToKey "</key>" "<true/> \n"
"</dict> \n"
" </array> \n";
CFMutableArrayRef AppleRAIDGetSetDescriptions(void)
{
CFStringRef errorString;
CFMutableArrayRef setDescriptions = (CFMutableArrayRef)IOCFUnserialize(raidDescriptionBuffer, kCFAllocatorDefault, 0, &errorString);
if (!setDescriptions) {
CFIndex bufferSize = CFStringGetLength(errorString);
bufferSize = CFStringGetMaximumSizeForEncoding(bufferSize, kCFStringEncodingUTF8) + 1;
char *buffer = malloc(bufferSize);
if (!buffer || !CFStringGetCString(errorString, buffer, bufferSize, kCFStringEncodingUTF8)) {
return NULL;
}
IOLog1("AppleRAIDGetSetDescriptions - failed while parsing raid definition file, error: %s\n", buffer);
CFRelease(errorString);
return NULL;
}
return setDescriptions;
}
static const char *defaultCreateSetBuffer =
" <dict> \n"
"<key>" kAppleRAIDHeaderVersionKey "</key>" "<integer size=\"32\">0x00020000</integer> \n"
"<key>" kAppleRAIDLevelNameKey "</key>" "<string>internal error</string> \n"
"<key>" kAppleRAIDSetNameKey "</key>" "<string>internal error</string> \n"
"<key>" kAppleRAIDSetUUIDKey "</key>" "<string>internal error</string> \n"
"<key>" kAppleRAIDSequenceNumberKey "</key>" "<integer size=\"32\">1</integer> \n"
"<key>" kAppleRAIDChunkSizeKey "</key>" "<integer size=\"64\">0x00008000</integer> \n"
"<key>" kAppleRAIDChunkCountKey "</key>" "<integer size=\"64\">0</integer> \n"
"<key>" kAppleRAIDMembersKey "</key>" "<array/> \n"
"<key>" kAppleRAIDSparesKey "</key>" "<array/> \n"
"<key>" kAppleRAIDSetAutoRebuildKey "</key>" "<false/> \n" "<key>" kAppleRAIDSetQuickRebuildKey "</key>" "<false/> \n" "<key>" kAppleRAIDSetTimeoutKey "</key>" "<integer size=\"32\">30</integer> \n"
"<key>" kAppleRAIDCanAddMembersKey "</key>" "<false/> \n" "<key>" kAppleRAIDCanAddSparesKey "</key>" "<false/> \n"
"<key>" kAppleRAIDSizesCanVaryKey "</key>" "<false/> \n" "<key>" kAppleRAIDRemovalAllowedKey "</key>" "<string>internal error</string> \n"
"<key>" kAppleRAIDSetContentHintKey "</key>" "<string/> \n"
" </dict> \n";
CFMutableDictionaryRef
AppleRAIDCreateSet(CFStringRef raidType, CFStringRef setName)
{
CFStringRef errorString;
CFMutableDictionaryRef setProps = (CFMutableDictionaryRef)IOCFUnserialize(defaultCreateSetBuffer, kCFAllocatorDefault, 0, &errorString);
if (!setProps) {
CFIndex bufferSize = CFStringGetLength(errorString);
bufferSize = CFStringGetMaximumSizeForEncoding(bufferSize, kCFStringEncodingUTF8) + 1;
char *buffer = malloc(bufferSize);
if (!buffer || !CFStringGetCString(errorString, buffer, bufferSize, kCFStringEncodingUTF8)) {
return NULL;
}
IOLog1("AppleRAIDCreateSet - failed while parsing create set template file, error: %s\n", buffer);
CFRelease(errorString);
return NULL;
}
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
if (!uuid) return NULL;
CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuid);
CFRelease(uuid);
if (!uuidString) return NULL;
CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDSetUUIDKey), uuidString); CFRelease(uuidString);
CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDLevelNameKey), raidType);
CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDSetNameKey), setName);
if (CFEqual(raidType, CFSTR("Stripe"))) {
CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDRemovalAllowedKey), CFSTR(kAppleRAIDRemovalNone));
}
if (CFEqual(raidType, CFSTR("Concat"))) {
CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDCanAddMembersKey), kCFBooleanTrue);
CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDSizesCanVaryKey), kCFBooleanTrue);
CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDRemovalAllowedKey), CFSTR(kAppleRAIDRemovalLastMember));
}
if (CFEqual(raidType, CFSTR("Mirror"))) {
CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDCanAddMembersKey), kCFBooleanTrue);
CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDCanAddSparesKey), kCFBooleanTrue);
CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDRemovalAllowedKey), CFSTR(kAppleRAIDRemovalAnyMember));
}
if (CFEqual(raidType, CFSTR("LVG"))) {
CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDCanAddMembersKey), kCFBooleanTrue);
CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDSizesCanVaryKey), kCFBooleanTrue);
CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDRemovalAllowedKey), CFSTR(kAppleRAIDRemovalAnyMember));
CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDSetContentHintKey), CFSTR(kAppleRAIDNoMediaExport));
}
return setProps;
}
bool
AppleRAIDModifySet(CFMutableDictionaryRef setProps, CFStringRef key, void * value)
{
CFStringRef errorString;
CFMutableDictionaryRef defaultSetProps = (CFMutableDictionaryRef)IOCFUnserialize(defaultCreateSetBuffer, kCFAllocatorDefault, 0, &errorString);
if (!defaultSetProps) {
CFIndex bufferSize = CFStringGetLength(errorString);
bufferSize = CFStringGetMaximumSizeForEncoding(bufferSize, kCFStringEncodingUTF8) + 1;
char *buffer = malloc(bufferSize);
if (!buffer || !CFStringGetCString(errorString, buffer, bufferSize, kCFStringEncodingUTF8)) {
goto error;
}
IOLog1("AppleRAIDModifySet - failed while parsing create set template file, error: %s\n", buffer);
CFRelease(errorString);
goto error;
}
const void * defaultValue = CFDictionaryGetValue(defaultSetProps, key);
if (!defaultValue) goto error;
if (CFGetTypeID(defaultValue) != CFGetTypeID(value)) goto error;
CFDictionarySetValue(setProps, key, value);
CFRelease(defaultSetProps);
return true;
error:
if (defaultSetProps) CFRelease(defaultSetProps);
return false;
}
AppleRAIDMemberRef
AppleRAIDAddMember(CFMutableDictionaryRef setProps, CFStringRef partitionName, CFStringRef memberType)
{
memberInfo_t * memberInfo = getMemberInfo(partitionName);
if (!memberInfo) return NULL;
if ((memberInfo->isWhole) && (!memberInfo->isRAID)) return NULL;
UInt32 version;
CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDHeaderVersionKey));
if (!number || !CFNumberGetValue(number, kCFNumberSInt32Type, &version) || version < 0x00020000) {
printf("AppleRAID: This operation is not supported on earlier RAID set revisions.\n");
return NULL;
}
CFStringRef uuidString = 0;
if (memberInfo->isRAID) {
uuidString = memberInfo->uuidString;
if (uuidString) CFRetain(uuidString);
} else {
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
if (!uuid) return NULL;
uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuid);
CFRelease(uuid);
}
freeMemberInfo(memberInfo);
if (!uuidString) return NULL;
CFMutableArrayRef uuidArray = (CFMutableArrayRef)CFDictionaryGetValue(setProps, memberType);
if (!uuidArray) return NULL;
uuidArray = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, uuidArray);
if (!uuidArray) return NULL;
CFDictionarySetValue(setProps, memberType, uuidArray);
CFStringRef pathArrayName = 0;
if (CFStringCompare(memberType, CFSTR(kAppleRAIDMembersKey), 0) == kCFCompareEqualTo) {
pathArrayName = CFSTR("_member names_");
}
if (CFStringCompare(memberType, CFSTR(kAppleRAIDSparesKey), 0) == kCFCompareEqualTo) {
pathArrayName = CFSTR("_spare names_");
}
if (!pathArrayName) return NULL;
CFMutableArrayRef pathArray = (CFMutableArrayRef)CFDictionaryGetValue(setProps, pathArrayName);
if (!pathArray) {
pathArray = (CFMutableArrayRef)CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
if (pathArray) CFDictionarySetValue(setProps, pathArrayName, pathArray);
}
if (!pathArray) return NULL;
CFArrayAppendValue(uuidArray, uuidString);
CFArrayAppendValue(pathArray, partitionName);
CFRelease(uuidString);
if (CFStringCompare(memberType, CFSTR(kAppleRAIDSparesKey), 0) == kCFCompareEqualTo) {
CFMutableStringRef status = (CFMutableStringRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDStatusKey));
if (status) {
if (CFStringCompare(status, CFSTR(kAppleRAIDStatusOnline), 0) == kCFCompareEqualTo) {
AppleRAIDModifySet(setProps, CFSTR(kAppleRAIDSetAutoRebuildKey), (void *)kCFBooleanTrue);
}
}
}
return (AppleRAIDMemberRef)uuidString;
}
#include <sys/fcntl.h>
static bool
writeHeader(CFMutableDictionaryRef memberProps, memberInfo_t * memberInfo)
{
AppleRAIDHeaderV2 * header = calloc(1, kAppleRAIDHeaderSize);
if (!header) return false;
strlcpy(header->raidSignature, kAppleRAIDSignature, sizeof(header->raidSignature));
CFStringRef string;
string = (CFStringRef)CFDictionaryGetValue(memberProps, CFSTR(kAppleRAIDSetUUIDKey));
if (string) CFStringGetCString(string, header->raidUUID, 64, kCFStringEncodingUTF8);
string = (CFStringRef)CFDictionaryGetValue(memberProps, CFSTR(kAppleRAIDMemberUUIDKey));
if (string) CFStringGetCString(string, header->memberUUID, 64, kCFStringEncodingUTF8);
header->size = memberInfo->chunkCount * memberInfo->chunkSize;
ByteSwapHeaderV2(header);
CFMutableDictionaryRef headerInfo = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, memberProps);
if (!headerInfo) return false;
CFIndex propCount = CFDictionaryGetCount(headerInfo);
if (!propCount) return false;
const void ** keys = calloc(propCount, sizeof(void *));
if (!keys) return false;
CFDictionaryGetKeysAndValues(headerInfo, keys, NULL);
CFIndex i;
for (i = 0; i < propCount; i++) {
if (!CFStringHasPrefix(keys[i], CFSTR("AppleRAID-"))) {
CFDictionaryRemoveValue(headerInfo, keys[i]);
}
}
CFDictionaryRemoveValue(headerInfo, CFSTR(kAppleRAIDSetQuickRebuildKey)); CFDictionaryRemoveValue(headerInfo, CFSTR(kAppleRAIDLVGExtentsKey)); CFDictionaryRemoveValue(headerInfo, CFSTR(kAppleRAIDLVGVolumeCountKey)); CFDictionaryRemoveValue(headerInfo, CFSTR(kAppleRAIDLVGFreeSpaceKey));
CFDataRef setData = IOCFSerialize(headerInfo, kNilOptions);
if (!setData) {
IOLog1("AppleRAIDLib - serialize on memberProps failed\n");
return false;
}
bcopy(CFDataGetBytePtr(setData), header->plist, CFDataGetLength(setData));
CFRelease(headerInfo);
CFRelease(setData);
int fd = open(memberInfo->devicePath, O_RDWR, 0);
if (fd < 0) return false;
IOLog1("writeHeader %s, header offset = %llu.\n", memberInfo->devicePath, memberInfo->headerOffset);
off_t seek = lseek(fd, memberInfo->headerOffset, SEEK_SET);
if (seek != memberInfo->headerOffset) goto ioerror;
int length = write(fd, header, kAppleRAIDHeaderSize);
if (length < kAppleRAIDHeaderSize) goto ioerror;
close(fd);
free(header);
return true;
ioerror:
close(fd);
free(header);
return false;
}
static CFDataRef
readHeader(memberInfo_t * memberInfo)
{
CFDataRef headerData = NULL;
int fd = open(memberInfo->devicePath, O_RDONLY, 0);
if (fd < 0) return NULL;
AppleRAIDHeaderV2 * header = calloc(1, kAppleRAIDHeaderSize);
if (!header) goto error;
off_t seek = lseek(fd, memberInfo->headerOffset, SEEK_SET);
if (seek != memberInfo->headerOffset) goto error;
int length = read(fd, header, kAppleRAIDHeaderSize);
if (length < kAppleRAIDHeaderSize) goto error;
if (!strncmp(header->raidSignature, kAppleRAIDSignature, sizeof(header->raidSignature))) {
headerData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)header, kAppleRAIDHeaderSize);
}
error:
close(fd);
if (header) free(header);
return headerData;
}
static AppleRAIDPrimaryOnDisk *
initPrimaryMetaDataForMirror(memberInfo_t * memberInfo)
{
if (!memberInfo->primaryMetaDataSize) return false;
void * bitmap = calloc(1, memberInfo->primaryMetaDataSize);
if (!bitmap) return NULL;
AppleRAIDPrimaryOnDisk * map = bitmap;
strlcpy(map->priMagic, kAppleRAIDPrimaryMagic, sizeof(map->priMagic));
map->priSize = memberInfo->primaryMetaDataSize;
map->priType = kAppleRAIDPrimaryExtents;
map->priSequenceNumber = 0; map->pri.extentCount = 1;
map->priUsed = sizeof(AppleRAIDPrimaryOnDisk);
AppleRAIDExtentOnDisk * extent = (AppleRAIDExtentOnDisk *)(map + 1);
extent->extentByteOffset = 0;
extent->extentByteCount = memberInfo->chunkCount * memberInfo->chunkSize;
map->priUsed += sizeof(AppleRAIDExtentOnDisk);
memberInfo->primaryData = bitmap;
return bitmap;
}
static AppleRAIDPrimaryOnDisk *
initPrimaryMetaDataForLVG(memberInfo_t * memberInfo)
{
if (!memberInfo->primaryMetaDataSize) return false;
void * primary = calloc(1, memberInfo->primaryMetaDataSize);
if (!primary) return NULL;
AppleRAIDPrimaryOnDisk * header = primary;
strlcpy(header->priMagic, kAppleRAIDPrimaryMagic, sizeof(header->priMagic));
header->priSize = memberInfo->primaryMetaDataSize;
header->priType = kAppleRAIDPrimaryLVG;
header->priSequenceNumber = 0; header->pri.volumeCount = 0;
header->priUsed = sizeof(AppleRAIDPrimaryOnDisk);
memberInfo->primaryData = primary;
return primary;
}
static CFMutableDictionaryRef initLogicalVolumeProps(CFStringRef lvgUUIDString, CFStringRef volumeType, UInt64 size,
CFStringRef location, CFNumberRef sequenceNumber, CFDataRef extentData);
static AppleLVMVolumeOnDisk * buildLVMetaDataBlock(CFMutableDictionaryRef lvProps, CFDataRef extentData);
static void *
initSecondaryMetaDataForLVG(memberInfo_t * memberInfo, CFMutableDictionaryRef setProps)
{
CFMutableDictionaryRef lvProps = 0;
AppleLVMVolumeOnDisk * lvData = 0;
void * secondary = 0;
if (!memberInfo->secondaryMetaDataSize || !setProps) return NULL;
CFStringRef lvgUUIDString = CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSetUUIDKey));
if (!lvgUUIDString) return NULL;
const void * sequenceProp = CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSequenceNumberKey));
if (!sequenceProp) return NULL;
CFStringRef volumeType = CFSTR(kAppleLVMVolumeTypeMaster);
UInt64 volumeSize = memberInfo->secondaryMetaDataSize;
AppleRAIDExtentOnDisk extent;
extent.extentByteOffset = memberInfo->chunkCount * memberInfo->chunkSize - memberInfo->secondaryMetaDataSize;
extent.extentByteCount = memberInfo->secondaryMetaDataSize;
CFDataRef extentData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)&extent, sizeof(AppleRAIDExtentOnDisk));
if (!extentData) goto error;
lvProps = initLogicalVolumeProps(lvgUUIDString, volumeType, volumeSize, CFSTR("meta"), sequenceProp, extentData);
if (!lvProps) goto error;
lvData = buildLVMetaDataBlock(lvProps, extentData);
if (!lvData) goto error;
secondary = calloc(1, memberInfo->secondaryMetaDataSize);
if (!secondary) goto error;
bcopy(lvData, secondary, lvData->lvHeaderSize);
if (lvProps) CFRelease(lvProps);
if (lvData) free(lvData);
if (extentData) CFRelease(extentData);
memberInfo->secondaryData = secondary;
return secondary;
error:
if (lvProps) CFRelease(lvProps);
if (lvData) free(lvData);
if (extentData) CFRelease(extentData);
if (secondary) free(secondary);
return NULL;
}
static bool
writePrimaryMetaData(memberInfo_t * memberInfo)
{
if (!memberInfo->primaryData) return false;
if (!memberInfo->primaryMetaDataSize) return false;
#if defined(__LITTLE_ENDIAN__)
AppleRAIDPrimaryOnDisk * header = memberInfo->primaryData;
if (header->priType == kAppleRAIDPrimaryExtents) {
int i;
AppleRAIDExtentOnDisk * extent = (AppleRAIDExtentOnDisk *)(header + 1);
for (i=0; i < header->pri.extentCount; i++) {
ByteSwapExtent(extent + i);
}
}
ByteSwapPrimaryHeader(header);
#endif
int fd = open(memberInfo->devicePath, O_RDWR, 0);
if (fd < 0) return false;
off_t metaDataOffset = memberInfo->chunkCount * memberInfo->chunkSize;
IOLog1("writePrimary %s, meta data offset = %llu.\n", memberInfo->devicePath, metaDataOffset);
off_t seek = lseek(fd, metaDataOffset, SEEK_SET);
if (seek != metaDataOffset) goto error;
int length = write(fd, memberInfo->primaryData, memberInfo->primaryMetaDataSize);
if (length < memberInfo->primaryMetaDataSize) goto error;
close(fd);
return true;
error:
close(fd);
return false;
}
#if 0
static AppleRAIDPrimaryOnDisk *
readPrimaryMetaData(memberInfo_t * memberInfo)
{
UInt64 primaryOffset = memberInfo->chunkSize * memberInfo->chunkCount;
UInt64 primarySize = memberInfo->primaryMetaDataSize;
if (memberInfo->primaryData) free(memberInfo->primaryData);
memberInfo->primaryData = NULL;
AppleRAIDPrimaryOnDisk * primary = calloc(1, primarySize);
if (!primary) return NULL;
int fd = open(memberInfo->devicePath, O_RDONLY, 0);
if (fd < 0) return NULL;
IOLog1("readPrimary %s, offset = %llu, size = %llu.\n", memberInfo->devicePath, primaryOffset, primarySize);
off_t seek = lseek(fd, primaryOffset, SEEK_SET);
if (seek != primaryOffset) goto error;
int length = read(fd, primary, primarySize);
if (length < primarySize) goto error;
if (!strncmp(primary->priMagic, kAppleRAIDPrimaryMagic, sizeof(primary->priMagic))) {
memberInfo->primaryData = primary;
} else {
IOLog1("readPrimary, found bad magic on %s.\n", memberInfo->devicePath);
}
error:
close(fd);
#if defined(__LITTLE_ENDIAN__)
AppleRAIDPrimaryOnDisk * header = memberInfo->primaryData;
ByteSwapPrimaryHeader(header);
if (header->priType == kAppleRAIDPrimaryExtents) {
int i;
AppleRAIDExtentOnDisk * extent = (AppleRAIDExtentOnDisk *)(header + 1);
for (i=0; i < header->pri.extentCount; i++) {
ByteSwapExtent(extent + i);
}
}
#endif
return memberInfo->primaryData;
}
#endif
static bool
writeSecondaryMetaData(memberInfo_t * memberInfo)
{
if (!memberInfo->secondaryData) return false;
if (!memberInfo->secondaryMetaDataSize) return false;
AppleLVMVolumeOnDisk * header = memberInfo->secondaryData;
AppleRAIDExtentOnDisk * extent = (AppleRAIDExtentOnDisk *)((char *)header + header->lvExtentsStart);
assert(header->lvExtentsCount == 1);
ByteSwapExtent(extent);
ByteSwapLVMVolumeHeader(header);
int fd = open(memberInfo->devicePath, O_RDWR, 0);
if (fd < 0) return false;
off_t metaDataOffset = memberInfo->chunkCount * memberInfo->chunkSize - memberInfo->secondaryMetaDataSize;
IOLog1("writeSecondary %s, meta data offset = %llu, size = %llu.\n",
memberInfo->devicePath, metaDataOffset, memberInfo->secondaryMetaDataSize);
off_t seek = lseek(fd, metaDataOffset, SEEK_SET);
if (seek != metaDataOffset) goto error;
int length = write(fd, memberInfo->secondaryData, memberInfo->secondaryMetaDataSize);
if (length < memberInfo->secondaryMetaDataSize) goto error;
close(fd);
return true;
error:
close(fd);
return false;
}
static bool
updateLiveSet(CFMutableDictionaryRef setProps)
{
CFStringRef setUUIDString = CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSetUUIDKey));
CFMutableDictionaryRef currentSet = AppleRAIDGetSetProperties(setUUIDString);
CFMutableDictionaryRef updatedInfo = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, setProps);
if (!updatedInfo) return false;
CFIndex propCount = CFDictionaryGetCount(updatedInfo);
const void **newKeys = (const void **)malloc(2 * propCount * sizeof(void *));
const void **newValues = newKeys + propCount;
CFDictionaryGetKeysAndValues(updatedInfo, newKeys, newValues);
CFIndex i;
for (i = 0; i < propCount; i++) {
const void * oldValue = 0;
if (CFDictionaryGetValueIfPresent(currentSet, newKeys[i], &oldValue)) {
if (CFEqual(newValues[i], oldValue)) {
CFDictionaryRemoveValue(updatedInfo, newKeys[i]);
}
}
}
propCount = CFDictionaryGetCount(updatedInfo);
if (!propCount) {
IOLog1("AppleRAID - updateLiveSet: nothing was changed in the set?\n");
return false;
}
CFDictionarySetValue(updatedInfo, CFSTR(kAppleRAIDSetUUIDKey), setUUIDString);
const void * seqNum = CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSequenceNumberKey));
if (!seqNum) return false;
CFDictionarySetValue(updatedInfo, CFSTR(kAppleRAIDSequenceNumberKey), seqNum);
CFDataRef setData = IOCFSerialize(updatedInfo, kNilOptions);
if (!setData) {
IOLog1("AppleRAID - updateLiveSet failed serializing on updatedInfo.\n");
return false;
}
io_connect_t raidControllerPort = AppleRAIDOpenConnection();
if (!raidControllerPort) {
IOLog1("AppleRAID - updateLiveSet - failed connecting to raid controller object?\n");
return false;
}
kern_return_t kr;
char * buffer = (char *)CFDataGetBytePtr(setData);
size_t bufferSize = CFDataGetLength(setData);
char updateData[0x1000];
size_t updateDataSize = sizeof(updateData);
if (!buffer) return false;
IOLog1("update set changes = %s\n", buffer);
kr = IOConnectCallStructMethod(raidControllerPort, kAppleRAIDUpdateSet, buffer, bufferSize, updateData, &updateDataSize);
if (kr != KERN_SUCCESS) {
IOLog1("AppleRAID - updateLiveSet failed with %x calling client.\n", kr);
AppleRAIDCloseConnection();
return false;
}
seqNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, (UInt32 *)updateData);
if (!seqNum) return false;
CFDictionarySetValue(setProps, CFSTR(kAppleRAIDSequenceNumberKey), seqNum);
CFRelease(setData);
CFRelease(updatedInfo);
AppleRAIDCloseConnection();
return true;
}
static bool
createNewMembers(CFMutableDictionaryRef setProps, memberInfo_t ** memberInfo,
CFIndex memberCount, CFIndex spareCount,
CFIndex newMemberCount, CFIndex newSpareCount)
{
if (!memberInfo) return false;
CFStringRef raidType = (CFStringRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDLevelNameKey));
bool isLVG = CFEqual(raidType, CFSTR(kAppleRAIDLevelNameLVG));
bool isMirror = CFEqual(raidType, CFSTR(kAppleRAIDLevelNameMirror));
UInt32 i;
for (i = 0; i < newMemberCount + newSpareCount; i++) {
if ((memberInfo[i]->isWhole) && (!memberInfo[i]->isRAID)) return false;
CFStringRef typeString= 0, uuidString = 0;
CFNumberRef index = 0, count = 0;
if (i < newMemberCount) {
typeString = CFSTR(kAppleRAIDMembersKey);
UInt32 memberIndex = i + memberCount;
index = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &memberIndex);
CFMutableArrayRef uuidArray = (CFMutableArrayRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDMembersKey));
if (!uuidArray) return false;
uuidString = (CFStringRef)CFArrayGetValueAtIndex(uuidArray, memberIndex);
} else {
typeString = CFSTR(kAppleRAIDSparesKey);
UInt32 spareIndex = kAppleRAIDDummySpareIndex;
index = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &spareIndex);
CFMutableArrayRef uuidArray = (CFMutableArrayRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSparesKey));
if (!uuidArray) return false;
spareIndex = i - newMemberCount + spareCount;
uuidString = (CFStringRef)CFArrayGetValueAtIndex(uuidArray, spareIndex);
}
count = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &memberInfo[i]->chunkCount);
if (typeString && index && uuidString && count) {
CFDictionarySetValue(setProps, CFSTR(kAppleRAIDMemberTypeKey), typeString);
CFDictionarySetValue(setProps, CFSTR(kAppleRAIDMemberIndexKey), index);
CFDictionarySetValue(setProps, CFSTR(kAppleRAIDMemberUUIDKey), uuidString);
CFDictionarySetValue(setProps, CFSTR(kAppleRAIDChunkCountKey), count);
CFRelease(index);
CFRelease(count);
} else {
return false;
}
if (memberInfo[i]->primaryMetaDataSize) {
AppleRAIDPrimaryOnDisk * primary = NULL;
if (isLVG) primary = initPrimaryMetaDataForLVG(memberInfo[i]);
if (isMirror) primary = initPrimaryMetaDataForMirror(memberInfo[i]);
if (!primary) {
IOLog1("AppleRAIDUpdateSet - failed to create the primary metadata for partition \"%s\"\n", memberInfo[i]->diskName);
return false;
}
const void * number = CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSequenceNumberKey));
if (!number) return false;
if (!CFNumberGetValue(number, kCFNumberSInt32Type, &primary->priSequenceNumber)) return false;
UInt64 usedSize = memberInfo[i]->primaryData->priUsed;
CFNumberRef meta1 = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &usedSize);
if (!meta1) return false;
CFDictionarySetValue(setProps, CFSTR(kAppleRAIDPrimaryMetaDataUsedKey), meta1);
CFRelease(meta1);
}
if (memberInfo[i]->secondaryMetaDataSize) {
void * secondary = NULL;
if (isLVG) secondary = initSecondaryMetaDataForLVG(memberInfo[i], setProps);
if (!secondary) {
IOLog1("AppleRAIDUpdateSet - failed to create the secondary metadata for partition \"%s\"\n", memberInfo[i]->diskName);
return false;
}
CFNumberRef meta2 = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &memberInfo[i]->secondaryMetaDataSize);
if (!meta2) return false;
CFDictionarySetValue(setProps, CFSTR(kAppleRAIDSecondaryMetaDataSizeKey), meta2);
CFRelease(meta2);
}
CFStringRef partitionName = CFStringCreateWithCString(kCFAllocatorDefault, memberInfo[i]->diskName, kCFStringEncodingUTF8);
if (!partitionName) return false;
bool success = AppleRAIDRemoveHeaders(partitionName);
if (!success) {
IOLog1("AppleRAIDUpdateSet - there was a problem erasing the raid headers on partition \"%s\"\n", memberInfo[i]->diskName);
return false;
}
CFRelease(partitionName);
if (memberInfo[i]->secondaryMetaDataSize && !writeSecondaryMetaData(memberInfo[i])) {
IOLog1("AppleRAIDUpdateSet - failed while writing secondary metadata to partition \"%s\"\n", memberInfo[i]->diskName);
return false;
}
if (memberInfo[i]->primaryMetaDataSize && !writePrimaryMetaData(memberInfo[i])) {
IOLog1("AppleRAIDUpdateSet - failed while writing primary metadata to partition \"%s\"\n", memberInfo[i]->diskName);
return false;
}
if (!writeHeader(setProps, memberInfo[i])) {
IOLog1("AppleRAIDUpdateSet - failed while writing RAID header to partition \"%s\"\n", memberInfo[i]->diskName);
return false;
}
if (memberInfo[i]->isRAID) {
CFMutableDictionaryRef updateInfo = CFDictionaryCreateMutable(kCFAllocatorDefault,
3, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!updateInfo) return false;
CFDictionarySetValue(updateInfo, CFSTR(kAppleRAIDSetUUIDKey), memberInfo[i]->uuidString);
UInt32 zero = 0;
CFNumberRef seqNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &zero);
if (!seqNum) return false;
CFDictionarySetValue(updateInfo, CFSTR(kAppleRAIDSequenceNumberKey), seqNum);
UInt32 subCommand = kAppleRAIDUpdateResetSet;
CFNumberRef updateSubCommand = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &subCommand);
CFDictionarySetValue(updateInfo, CFSTR("_update command_"), updateSubCommand);
updateLiveSet(updateInfo);
}
}
return true;
}
static UInt64 calculateBitMapSize(UInt64 partitionSize, UInt64 chunkSize, UInt64 * remainingBytes);
AppleRAIDSetRef
AppleRAIDUpdateSet(CFMutableDictionaryRef setProps)
{
CFStringRef setUUIDString = CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSetUUIDKey));
CFRetain(setUUIDString);
memberInfo_t ** memberInfo = 0;
#if DEBUG
CFShow(setUUIDString);
#endif
CFIndex memberCount = 0, spareCount = 0;
CFIndex newMemberCount = 0, newSpareCount = 0;
CFMutableArrayRef newMemberNames = (CFMutableArrayRef)CFDictionaryGetValue(setProps, CFSTR("_member names_"));
if (newMemberNames) {
CFRetain(newMemberNames);
CFDictionaryRemoveValue(setProps, CFSTR("_member names_"));
newMemberCount = CFArrayGetCount(newMemberNames);
}
CFMutableArrayRef newSpareNames = (CFMutableArrayRef)CFDictionaryGetValue(setProps, CFSTR("_spare names_"));
if (newSpareNames) {
CFRetain(newSpareNames);
CFDictionaryRemoveValue(setProps, CFSTR("_spare names_"));
newSpareCount = CFArrayGetCount(newSpareNames);
}
bool liveSet = CFDictionaryContainsKey(setProps, CFSTR(kAppleRAIDStatusKey)); if (liveSet) {
CFDictionaryRemoveValue(setProps, CFSTR(kAppleRAIDStatusKey));
CFMutableArrayRef tempMembers = (CFMutableArrayRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDMembersKey));
if (tempMembers) {
memberCount = CFArrayGetCount(tempMembers) - newMemberCount;
}
CFMutableArrayRef tempSpares = (CFMutableArrayRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSparesKey));
if (tempSpares) {
spareCount = CFArrayGetCount(tempSpares) - newSpareCount;
}
}
if (newSpareCount || newMemberCount) {
memberInfo = calloc(newMemberCount + newSpareCount, sizeof(memberInfo_t *));
if (!memberInfo) return NULL;
UInt32 i;
for (i = 0; i < newMemberCount + newSpareCount; i++) {
CFStringRef diskName;
if (i < newMemberCount) {
diskName = (CFStringRef)CFArrayGetValueAtIndex(newMemberNames, i);
} else {
diskName = (CFStringRef)CFArrayGetValueAtIndex(newSpareNames, i - newMemberCount);
}
memberInfo[i] = getMemberInfo(diskName);
if (!memberInfo[i]) return NULL;
#ifdef DEBUG
if (memberInfo[i]) {
IOLog1("\t%s: regName = \"%s\" size = %lld block size = %lld whole = %s raid = %s uuid = %p\n",
memberInfo[i]->diskName, memberInfo[i]->regName, memberInfo[i]->size, memberInfo[i]->blockSize,
memberInfo[i]->isWhole?"true":"false", memberInfo[i]->isRAID?"true":"false", memberInfo[i]->uuidString);
}
#endif
}
if (newMemberNames) CFRelease(newMemberNames);
if (newSpareNames) CFRelease(newSpareNames);
bool sizesCanVary = (CFBooleanRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSizesCanVaryKey)) == kCFBooleanTrue;
bool quickRebuild = (CFBooleanRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSetQuickRebuildKey)) == kCFBooleanTrue;
CFStringRef raidType = (CFStringRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDLevelNameKey));
bool isLVG = CFEqual(raidType, CFSTR(kAppleRAIDLevelNameLVG));
UInt64 metaDataSize = 0;
if (quickRebuild) {
if (liveSet) {
CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDPrimaryMetaDataUsedKey));
if (!number || !CFNumberGetValue(number, kCFNumberSInt64Type, &metaDataSize) || !metaDataSize) {
printf("AppleRAID: Failed to find the size of the mirror quick rebuild bitmap.\n");
return NULL;
}
}
}
UInt64 chunkSize = 0;
CFNumberRef number;
number = (CFNumberRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDChunkSizeKey));
if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &chunkSize);
if (!chunkSize) return NULL;
UInt64 smallestSize = 0;
if (liveSet) {
UInt64 chunkCount = 0;
CFNumberRef number;
number = (CFNumberRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDChunkCountKey));
if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &chunkCount);
if (!chunkCount) return NULL;
smallestSize = chunkCount * chunkSize + metaDataSize + (UInt64)kAppleRAIDHeaderSize;
} else {
smallestSize = memberInfo[0]->size;
}
if (!sizesCanVary) {
for (i = 0; i < newMemberCount + newSpareCount; i++) {
if (liveSet) {
if (memberInfo[i]->size < smallestSize) {
IOLog1("AppleRAIDUpdateSet() new member is too small to add to set.\n");
return NULL;
}
} else {
if (memberInfo[i]->size < smallestSize) smallestSize = memberInfo[i]->size;
}
}
IOLog1("smallest member size %lld\n", smallestSize);
}
if (quickRebuild && !metaDataSize) {
metaDataSize = calculateBitMapSize(smallestSize, chunkSize, NULL);
IOLog1("quick rebuild bit map size = %lld @ offset %lld\n", metaDataSize,
(ARHEADER_OFFSET(smallestSize) - metaDataSize) / chunkSize);
}
if (isLVG) metaDataSize = 0x100000;
for (i = 0; i < newMemberCount + newSpareCount; i++) {
memberInfo[i]->chunkSize = chunkSize;
memberInfo[i]->primaryMetaDataSize = metaDataSize;
if (isLVG) memberInfo[i]->secondaryMetaDataSize = 0x400000;
if (sizesCanVary) {
memberInfo[i]->chunkCount = (memberInfo[i]->headerOffset - metaDataSize) / chunkSize;
} else {
memberInfo[i]->chunkCount = (ARHEADER_OFFSET(smallestSize) - metaDataSize) / chunkSize;
}
}
}
if (liveSet) {
if (!updateLiveSet(setProps)) return NULL;
}
if (newSpareCount || newMemberCount) {
if (!createNewMembers(setProps, memberInfo, memberCount,
spareCount, newMemberCount, newSpareCount)) return NULL;
}
if (newSpareCount || newMemberCount) {
UInt32 i;
for (i=0; i < newSpareCount + newMemberCount; i++) {
freeMemberInfo(memberInfo[i]);
}
free(memberInfo);
}
return setUUIDString;
}
bool
AppleRAIDRemoveHeaders(CFStringRef partitionName)
{
memberInfo_t * memberInfo = getMemberInfo(partitionName);
if (!memberInfo) return false;
AppleRAIDHeaderV2 * header = calloc(1, kAppleRAIDHeaderSize);
if (!header) return false;
int fd = open(memberInfo->devicePath, O_RDWR, 0);
if (fd < 0) return false;
UInt64 headerOffset = 0;
{
IOLog2("AppleRAIDRemoveHeaders %s, scaning header offset = %llu.\n", memberInfo->devicePath, headerOffset);
off_t seek = lseek(fd, headerOffset, SEEK_SET);
if (seek != headerOffset) return false;
int length = read(fd, header, kAppleRAIDHeaderSize);
if (length < kAppleRAIDHeaderSize) return false;
if (!strncmp(header->raidSignature, kAppleRAIDSignature, sizeof(header->raidSignature))) {
IOLog1("AppleRAIDRemoveHeaders %s, found ARv1 header at offset = %llu.\n", memberInfo->devicePath, headerOffset);
bzero(header, kAppleRAIDHeaderSize);
seek = lseek(fd, headerOffset, SEEK_SET);
if (seek != headerOffset) return false;
length = write(fd, header, kAppleRAIDHeaderSize);
if (length < kAppleRAIDHeaderSize) return false;
}
}
headerOffset = ARHEADER_OFFSET(memberInfo->size);
int count = 5;
while (headerOffset && count) {
IOLog2("AppleRAIDRemoveHeaders %s, scanning header offset = %llu.\n", memberInfo->devicePath, headerOffset);
off_t seek = lseek(fd, headerOffset, SEEK_SET);
if (seek != headerOffset) break;
int length = read(fd, header, kAppleRAIDHeaderSize);
if (length < kAppleRAIDHeaderSize) break;
ByteSwapHeaderV2(header);
if (!strncmp(header->raidSignature, kAppleRAIDSignature, sizeof(header->raidSignature))) {
IOLog1("AppleRAIDRemoveHeaders %s, found ARv2 header at offset = %llu.\n", memberInfo->devicePath, headerOffset);
UInt64 memberSize = header->size;
bzero(header, kAppleRAIDHeaderSize);
seek = lseek(fd, headerOffset, SEEK_SET);
if (seek != headerOffset) break;
length = write(fd, header, kAppleRAIDHeaderSize);
if (length < kAppleRAIDHeaderSize) break;
headerOffset = (memberSize < headerOffset) ? ARHEADER_OFFSET(memberSize) : 0;
} else {
headerOffset = 0;
}
count--;
}
close(fd);
freeMemberInfo(memberInfo);
return headerOffset == 0;
}
bool
AppleRAIDRemoveMember(CFMutableDictionaryRef setProps, AppleRAIDMemberRef member)
{
UInt32 version;
CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDHeaderVersionKey));
if (!number || !CFNumberGetValue(number, kCFNumberSInt32Type, &version) || version < 0x00020000) {
printf("AppleRAID: This operation is not supported on earlier RAID set revisions.\n");
return NULL;
}
CFMutableArrayRef uuidArray = (CFMutableArrayRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDMembersKey));
CFMutableArrayRef uuidArray2 = 0;
if (!uuidArray) return NULL;
CFIndex count = 0;
CFIndex index;
again:
count = CFArrayGetCount(uuidArray);
for (index = 0; index < count; index++) {
CFStringRef uuidString = (CFStringRef)CFArrayGetValueAtIndex(uuidArray, index);
if (CFStringCompare(member, uuidString, 0) == kCFCompareEqualTo) {
CFArraySetValueAtIndex(uuidArray, index, CFSTR(kAppleRAIDDeletedUUID));
return true;
}
}
if (!uuidArray2) {
uuidArray2 = (CFMutableArrayRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSparesKey));
if (uuidArray2 && CFArrayGetCount(uuidArray2)) {
uuidArray = uuidArray2;
goto again;
}
}
return false;
}
bool
AppleRAIDDestroySet(AppleRAIDSetRef setName)
{
CFMutableDictionaryRef setProps = AppleRAIDGetSetProperties(setName);
if (!setProps) return false;
UInt32 subCommand = kAppleRAIDUpdateDestroySet;
CFNumberRef destroySubCommand = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &subCommand);
CFDictionarySetValue(setProps, CFSTR("_update command_"), destroySubCommand);
if (!updateLiveSet(setProps)) return false;
CFRelease(setProps);
return true;
}
#define kAppleRAIDMinBitMapBytesPerBit (512 * 1024) // min bytes allowed to be represented by one bit
#define kAppleRAIDMaxBitMapBytesPerBit (32 * 1024 * 1024) // max bytes allowed to be represented by one bit
#define kAppleRAIDBitMapPageSize (4 * 1024) // the "offical" raid page size
#define kAppleRAIDMinBitMapSize (32 * 4 * 1024) // 128k
static UInt64 calculateBitMapSize(UInt64 partitionSize, UInt64 chunkSize, UInt64 * remainingBytes)
{
UInt64 bytesPerBit = kAppleRAIDMinBitMapBytesPerBit;
UInt64 bitMapSize = kAppleRAIDMinBitMapSize;
UInt64 bitsNeeded;
if (remainingBytes) {
UInt64 availableBytes = ARHEADER_OFFSET(partitionSize) - sizeof(AppleRAIDPrimaryOnDisk);
bitsNeeded = (availableBytes - bitMapSize) / bytesPerBit;
bitsNeeded += (availableBytes - bitMapSize) % bytesPerBit ? 1 : 0;
while (bitsNeeded > (bitMapSize * 8)) {
bytesPerBit *= 2;
if (bytesPerBit > kAppleRAIDMaxBitMapBytesPerBit) {
bytesPerBit = kAppleRAIDMinBitMapBytesPerBit;
bitMapSize += kAppleRAIDMinBitMapSize;
}
bitsNeeded = (availableBytes - bitMapSize) / bytesPerBit;
bitsNeeded += (availableBytes - bitMapSize) % bytesPerBit ? 1 : 0;
}
*remainingBytes = (availableBytes - bitMapSize) / chunkSize * chunkSize;
} else {
bitsNeeded = partitionSize / bytesPerBit;
bitsNeeded += partitionSize % bytesPerBit ? 1 : 0;
while (bitsNeeded > (bitMapSize * 8)) {
bytesPerBit *= 2;
if (bytesPerBit > kAppleRAIDMaxBitMapBytesPerBit) {
bytesPerBit = kAppleRAIDMinBitMapBytesPerBit;
bitMapSize += kAppleRAIDMinBitMapSize;
}
bitsNeeded = partitionSize / bytesPerBit;
bitsNeeded += partitionSize % bytesPerBit ? 1 : 0;
}
}
return bitMapSize;
}
static AppleRAIDExtentOnDisk *
allocateExtent(AppleRAIDExtentOnDisk * firstExtent, UInt64 lvgExtentCount, UInt64 size, CFStringRef location, UInt64 * extentCount)
{
*extentCount = 0;
AppleRAIDExtentOnDisk dummyExtent = {0, 0};
AppleRAIDExtentOnDisk * newExtents = malloc(sizeof(AppleRAIDExtentOnDisk));
if (!newExtents) return NULL;
while (size) {
AppleRAIDExtentOnDisk * prevExtent = &dummyExtent;
AppleRAIDExtentOnDisk * nextExtent = firstExtent;
AppleRAIDExtentOnDisk * prevLargestExtent = 0;
AppleRAIDExtentOnDisk * nextLargestExtent = 0;
UInt64 gap = 0;
UInt64 largestGap = 0;
while (nextExtent < firstExtent + lvgExtentCount) {
gap = nextExtent->extentByteOffset - (prevExtent->extentByteOffset + prevExtent->extentByteCount);
if (gap >= size) break;
if (gap > largestGap) {
largestGap = gap;
prevLargestExtent = prevExtent;
nextLargestExtent = nextExtent;
}
prevExtent = nextExtent;
nextExtent++;
}
if (largestGap && gap < size) {
prevExtent = prevLargestExtent;
nextExtent = nextLargestExtent;
gap = nextExtent->extentByteOffset - (prevExtent->extentByteOffset + prevExtent->extentByteCount);
IOLog1("largest extent found is %lld, wanted %lld\n", gap, size);
}
if (!gap) {
free(newExtents);
return NULL;
}
if (gap) {
if (*extentCount) {
newExtents = reallocf(newExtents, sizeof(AppleRAIDExtentOnDisk) * (*extentCount + 1));
if (!newExtents) return NULL;
}
newExtents[*extentCount].extentByteOffset = prevExtent->extentByteOffset + prevExtent->extentByteCount;
newExtents[*extentCount].extentByteCount = MIN(gap, size);
IOLog1("Allocated new extent at %lld, size %lld\n", newExtents[*extentCount].extentByteOffset, newExtents[*extentCount].extentByteCount);
prevExtent->extentByteCount += MIN(gap, size);
*extentCount += 1;
size -= MIN(gap, size);
}
}
if (!size) return newExtents;
free(newExtents);
return NULL;
}
static UInt64 growLastExtent(CFMutableDataRef extentData, AppleRAIDExtentOnDisk * lvgExtentList, UInt64 lvgExtentCount, UInt64 newSize)
{
CFIndex extentDataSize = CFDataGetLength(extentData);
CFIndex index = 0;
UInt64 volumeSize = 0;
CFRange range;
AppleRAIDExtentOnDisk foo, * extent = &foo;
while (index < extentDataSize) {
range = CFRangeMake(index, sizeof(AppleRAIDExtentOnDisk));
CFDataGetBytes(extentData, range, (void *)extent);
volumeSize += extent->extentByteCount;
index += sizeof(AppleRAIDExtentOnDisk);
}
UInt64 volumeEnd = extent->extentByteOffset + extent->extentByteCount;
UInt64 bytesNeeded = newSize - volumeSize;
AppleRAIDExtentOnDisk * lvgExtent;
index = 0;
UInt64 gapStart = 0;
UInt64 gapSize;
while (gapStart <= volumeEnd && index < (lvgExtentCount - 1)) {
lvgExtent = lvgExtentList + index;
gapStart = lvgExtent->extentByteOffset + lvgExtent->extentByteCount;
gapSize = (lvgExtent + 1)->extentByteOffset - gapStart;
if (gapStart == volumeEnd && gapSize) {
UInt64 bytesAvailable = MIN(bytesNeeded, gapSize);
extent->extentByteCount += bytesAvailable;
lvgExtent->extentByteCount += bytesAvailable; CFDataReplaceBytes(extentData, range, (void *)extent, sizeof(AppleRAIDExtentOnDisk));
return volumeSize + bytesAvailable;
}
index++;
}
return 0;
}
static UInt64 truncateExtents(CFMutableDataRef extentData, UInt64 newSize)
{
CFIndex extentDataSize = CFDataGetLength(extentData);
CFIndex index = 0;
UInt64 extentEnd = 0;
CFRange range;
AppleRAIDExtentOnDisk foo, * extent = &foo;
while (index < extentDataSize) {
range = CFRangeMake(index, sizeof(AppleRAIDExtentOnDisk));
CFDataGetBytes(extentData, range, (void *)extent);
extentEnd += extent->extentByteCount;
if (newSize <= extentEnd) {
extent->extentByteCount -= extentEnd - newSize;
CFDataReplaceBytes(extentData, range, (void *)extent, sizeof(AppleRAIDExtentOnDisk));
CFDataSetLength(extentData, index + sizeof(AppleRAIDExtentOnDisk));
return CFDataGetLength(extentData) / sizeof(AppleRAIDExtentOnDisk); }
index += sizeof(AppleRAIDExtentOnDisk);
}
return 0;
}
UInt64 AppleRAIDGetUsableSize(UInt64 partitionSize, UInt64 chunkSize, UInt32 options)
{
UInt64 usable = 0;
if (!chunkSize) {
IOLog1("AppleRAIDGetUseableSize: zero chunkSize?\n");
return 0;
}
switch (options) {
case kAppleRAIDUsableSizeOptionNone:
usable = ARHEADER_OFFSET(partitionSize) / chunkSize * chunkSize;
break;
case kAppleRAIDUsableSizeOptionQuickRebuild:
(void)calculateBitMapSize(partitionSize, chunkSize, &usable);
break;
default:
break;
}
return usable;
}
CFDataRef
AppleRAIDDumpHeader(CFStringRef partitionName)
{
memberInfo_t * memberInfo = getMemberInfo(partitionName);
if (!memberInfo) return NULL;
CFDataRef data = readHeader(memberInfo);
freeMemberInfo(memberInfo);
return data;
}
CFMutableArrayRef
AppleLVMGetVolumesForGroup(AppleRAIDSetRef setRef, AppleRAIDMemberRef member)
{
kern_return_t kr;
size_t propSize = kMaxIOConnectTransferSize; CFMutableArrayRef volumes = NULL;
size_t bufferSize = kAppleRAIDMaxUUIDStringSize * 2;
char buffer[bufferSize];
if (!CFStringGetCString(setRef, buffer, kAppleRAIDMaxUUIDStringSize, kCFStringEncodingUTF8)) {
IOLog1("AppleLVMGetVolumesForGroup() CFStringGetCString failed on set ref?\n");
return NULL;
}
if (member) {
if (!CFStringGetCString(member, &buffer[kAppleRAIDMaxUUIDStringSize], kAppleRAIDMaxUUIDStringSize, kCFStringEncodingUTF8)) {
IOLog1("AppleLVMGetVolumesForGroup() CFStringGetCString failed on member ref?\n");
return NULL;
}
} else {
buffer[kAppleRAIDMaxUUIDStringSize] = 0;
}
io_connect_t raidControllerPort = AppleRAIDOpenConnection();
if (!raidControllerPort) return NULL;
char * propString = (char *)malloc(propSize);
kr = IOConnectCallStructMethod(raidControllerPort, kAppleLVMGetVolumesForGroup, buffer, bufferSize, propString, &propSize);
if (kr == KERN_SUCCESS) {
IOLog2("AppleLVMGetVolumesForGroup was successful.\n");
IOLog2("size = %d, prop = %s\n", (int)propSize, (char *)propString);
volumes = (CFMutableArrayRef)IOCFUnserialize(propString, kCFAllocatorDefault, 0, NULL);
} else {
IOLog1("AppleLVMGetVolumesForGroup failed with 0x%x.\n", kr);
}
free(propString);
AppleRAIDCloseConnection();
return volumes;
}
CFMutableDictionaryRef
AppleLVMGetVolumeProperties(AppleLVMVolumeRef volRef)
{
kern_return_t kr;
size_t propSize = kMaxIOConnectTransferSize;
CFMutableDictionaryRef props = NULL;
size_t bufferSize = kAppleRAIDMaxUUIDStringSize;
char buffer[bufferSize];
if (!CFStringGetCString(volRef, buffer, bufferSize, kCFStringEncodingUTF8)) {
IOLog1("AppleLVMGetVolumeProperties() CFStringGetCString failed?\n");
return NULL;
}
io_connect_t raidControllerPort = AppleRAIDOpenConnection();
if (!raidControllerPort) return NULL;
char * propString = (char *)malloc(propSize);
kr = IOConnectCallStructMethod(raidControllerPort, kAppleLVMGetVolumeProperties, buffer, bufferSize, propString, &propSize);
if (kr == KERN_SUCCESS) {
IOLog2("AppleLVMGetVolumeProperties was successful.\n");
IOLog2("size = %d, prop = %s\n", (int)propSize, (char *)propString);
props = (CFMutableDictionaryRef)IOCFUnserialize(propString, kCFAllocatorDefault, 0, NULL);
}
free(propString);
AppleRAIDCloseConnection();
return props;
}
static AppleRAIDExtentOnDisk *
getVolumeExtents(AppleLVMVolumeRef volRef, UInt64 * extentCount)
{
kern_return_t kr;
size_t bufferSize = kAppleRAIDMaxUUIDStringSize;
char buffer[bufferSize];
size_t extentSize = kMaxIOConnectTransferSize;
AppleRAIDExtentOnDisk * extents = NULL;
if (!extentCount || !*extentCount) return NULL;
if (!CFStringGetCString(volRef, buffer, bufferSize, kCFStringEncodingUTF8)) {
IOLog1("AppleLVMGetVolumeExtents() CFStringGetCString failed?\n");
return NULL;
}
if (*extentCount * sizeof(AppleRAIDExtentOnDisk) > extentSize) return NULL;
io_connect_t raidControllerPort = AppleRAIDOpenConnection();
if (!raidControllerPort) return NULL;
AppleRAIDExtentOnDisk * extentsBuffer = (AppleRAIDExtentOnDisk *)malloc(extentSize);
kr = IOConnectCallStructMethod(raidControllerPort, kAppleLVMGetVolumeExtents, buffer, bufferSize, extentsBuffer, &extentSize);
if (kr == KERN_SUCCESS) {
IOLog2("AppleLVMGetVolumeExtents was successful.\n");
IOLog2("size = %d, extent = %s\n", (int)extentSize, (char *)extentString);
extents = extentsBuffer;
*extentCount = extentSize / sizeof(AppleRAIDExtentOnDisk);
} else {
IOLog2("AppleLVMGetVolumeExtents failed.\n");
free(extentsBuffer);
}
AppleRAIDCloseConnection();
return extents;
}
CFDataRef AppleLVMGetVolumeExtents(AppleLVMVolumeRef volRef)
{
UInt64 extentCount = kMaxIOConnectTransferSize / sizeof(AppleRAIDExtentOnDisk);
AppleRAIDExtentOnDisk * extentList = getVolumeExtents(volRef, &extentCount);
if (!extentList) return NULL;
if (extentList->extentByteCount == 0) {
extentCount = extentList->extentByteOffset;
extentList = getVolumeExtents(volRef, &extentCount);
}
if (!extentList) return NULL;
CFDataRef extentData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)extentList,
extentCount * sizeof(AppleRAIDExtentOnDisk), kCFAllocatorMalloc);
return extentData;
}
static const char *lvDescriptionBuffer =
" <array> \n"
"<dict> \n"
"<key>" kAppleLVMVolumeTypeKey "</key>" "<string>" kAppleLVMVolumeTypeConcat "</string> \n"
"</dict> \n"
"<dict> \n"
"<key>" kAppleLVMVolumeTypeKey "</key>" "<string>" kAppleLVMVolumeTypeSnapRO "</string> \n"
"</dict> \n"
"<dict> \n"
"<key>" kAppleLVMVolumeTypeKey "</key>" "<string>" kAppleLVMVolumeTypeSnapRW "</string> \n"
"</dict> \n"
" </array> \n";
CFMutableArrayRef AppleLVMGetVolumeDescription(void)
{
CFStringRef errorString;
CFMutableArrayRef lvDescription = (CFMutableArrayRef)IOCFUnserialize(lvDescriptionBuffer, kCFAllocatorDefault, 0, &errorString);
if (!lvDescription) {
CFIndex bufferSize = CFStringGetLength(errorString);
bufferSize = CFStringGetMaximumSizeForEncoding(bufferSize, kCFStringEncodingUTF8) + 1;
char *buffer = malloc(bufferSize);
if (!buffer || !CFStringGetCString(errorString, buffer, bufferSize, kCFStringEncodingUTF8)) {
return NULL;
}
IOLog1("AppleLVMGetVolumeDescription - failed while parsing raid definition file, error: %s\n", buffer);
CFRelease(errorString);
return NULL;
}
return lvDescription;
}
static const char *defaultCreateLVBuffer =
" <dict> \n"
"<key>" kAppleLVMVolumeVersionKey "</key>" "<integer size=\"32\">0x00030000</integer> \n"
"<key>" kAppleLVMGroupUUIDKey "</key>" "<string>internal error</string> \n"
"<key>" kAppleLVMVolumeUUIDKey "</key>" "<string>internal error</string> \n"
"<key>" kAppleLVMVolumeSequenceKey "</key>" "<integer size=\"32\">0</integer> \n"
"<key>" kAppleLVMVolumeSizeKey "</key>" "<integer size=\"64\">0x00000000</integer> \n"
"<key>" kAppleLVMVolumeExtentCountKey "</key>" "<integer size=\"64\">0x00000001</integer> \n"
"<key>" kAppleLVMVolumeTypeKey "</key>" "<string>internal error</string> \n"
"<key>" kAppleLVMVolumeLocationKey "</key>" "<string/> \n"
"<key>" kAppleLVMVolumeContentHintKey "</key>" "<string/> \n"
"<key>" kAppleLVMVolumeNameKey "</key>" "<string/> \n"
" </dict> \n";
static CFMutableDictionaryRef
initLogicalVolumeProps(CFStringRef lvgUUIDString, CFStringRef volumeType, UInt64 size, CFStringRef location,
CFNumberRef sequenceNumber, CFDataRef extentData)
{
CFStringRef errorString;
UInt64 extentCount = CFDataGetLength(extentData) / sizeof(AppleRAIDExtentOnDisk);
if (!extentCount) return NULL;
CFMutableDictionaryRef lvProps = (CFMutableDictionaryRef)IOCFUnserialize(defaultCreateLVBuffer, kCFAllocatorDefault, 0, &errorString);
if (!lvProps) {
CFIndex bufferSize = CFStringGetLength(errorString);
bufferSize = CFStringGetMaximumSizeForEncoding(bufferSize, kCFStringEncodingUTF8) + 1;
char *buffer = malloc(bufferSize);
if (!buffer || !CFStringGetCString(errorString, buffer, bufferSize, kCFStringEncodingUTF8)) {
return NULL;
}
IOLog1("AppleLVMCreateVolume - failed while parsing logical volume template file, error: %s\n", buffer);
CFRelease(errorString);
return NULL;
}
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
if (!uuid) return NULL;
CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuid);
CFRelease(uuid);
if (!uuidString) return NULL;
CFNumberRef sizeProp = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &size);
if (!sizeProp) return NULL;
CFNumberRef countProp = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &extentCount);
if (!countProp) return NULL;
CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeUUIDKey), uuidString);
CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMGroupUUIDKey), lvgUUIDString);
CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeSequenceKey), sequenceNumber);
CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeSizeKey), sizeProp);
CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeLocationKey), location);
CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeTypeKey), volumeType);
CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeExtentCountKey), countProp);
CFDictionarySetValue(lvProps, CFSTR("_extent data_"), extentData);
CFRelease(uuidString);
CFRelease(sizeProp);
CFRelease(countProp);
return lvProps;
}
CFMutableDictionaryRef
AppleLVMCreateVolume(AppleRAIDSetRef setRef, CFStringRef volumeType, UInt64 volumeSize, CFStringRef volumeLocation)
{
CFMutableDictionaryRef lvProps = 0;
if (!setRef || !volumeType || !volumeSize || !volumeLocation) return NULL;
setInfo_t * lvgInfo = getSetInfo(setRef);
if (!lvgInfo) return NULL;
UInt64 lvgExtentCount = 0;
CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(lvgInfo->setProps, CFSTR(kAppleRAIDLVGExtentsKey));
if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &lvgExtentCount);
UInt64 lvgFreeSpace = 0;
number = (CFNumberRef)CFDictionaryGetValue(lvgInfo->setProps, CFSTR(kAppleRAIDLVGFreeSpaceKey));
if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &lvgFreeSpace);
if (volumeSize > lvgFreeSpace) {
printf("AppleRAID: Insufficent free space to create requested logical volume.\n");
return NULL;
}
AppleRAIDExtentOnDisk * lvgExtentList = getVolumeExtents(setRef, &lvgExtentCount);
if (!lvgExtentList) goto error;
UInt64 extentCount = 0;
AppleRAIDExtentOnDisk * extentList = allocateExtent(lvgExtentList, lvgExtentCount, volumeSize, volumeLocation, &extentCount);
if (!extentList) goto error;
CFDataRef extentData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)extentList,
extentCount * sizeof(AppleRAIDExtentOnDisk), kCFAllocatorMalloc);
if (!extentData) goto error;
const void * sequenceProp = CFDictionaryGetValue(lvgInfo->setProps, CFSTR(kAppleRAIDSequenceNumberKey));
if (!sequenceProp) goto error;
CFStringRef lvgUUIDString = CFDictionaryGetValue(lvgInfo->setProps, CFSTR(kAppleRAIDSetUUIDKey));
if (!lvgUUIDString) goto error;
lvProps = initLogicalVolumeProps(lvgUUIDString, volumeType, volumeSize, volumeLocation, sequenceProp, extentData);
if (!lvProps) goto error;
freeSetInfo(lvgInfo);
return lvProps;
error:
if (lvProps) CFRelease(lvProps);
freeSetInfo(lvgInfo);
return NULL;
}
bool
AppleLVMModifyVolume(CFMutableDictionaryRef lvProps, CFStringRef key, void * value)
{
CFStringRef errorString;
CFMutableDictionaryRef defaultLVProps = (CFMutableDictionaryRef)IOCFUnserialize(defaultCreateLVBuffer, kCFAllocatorDefault, 0, &errorString);
if (!defaultLVProps) {
CFIndex bufferSize = CFStringGetLength(errorString);
bufferSize = CFStringGetMaximumSizeForEncoding(bufferSize, kCFStringEncodingUTF8) + 1;
char *buffer = malloc(bufferSize);
if (!buffer || !CFStringGetCString(errorString, buffer, bufferSize, kCFStringEncodingUTF8)) {
goto error;
}
IOLog1("AppleLVMModifyVolume - failed while parsing logical volume template file, error: %s\n", buffer);
CFRelease(errorString);
goto error;
}
const void * defaultValue = CFDictionaryGetValue(defaultLVProps, key);
if (!defaultValue) goto error;
if (CFGetTypeID(defaultValue) != CFGetTypeID(value)) goto error;
AppleRAIDSetRef lvgRef = (CFStringRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMGroupUUIDKey));
if (!lvgRef) return false;
CFMutableDictionaryRef lvgProps = AppleRAIDGetSetProperties(lvgRef);
if (!lvgProps) return false;
const void * sequenceNumber = CFDictionaryGetValue(lvgProps, CFSTR(kAppleRAIDSequenceNumberKey));
if (!sequenceNumber) return false;
CFDictionarySetValue(lvProps, CFSTR(kAppleLVMVolumeSequenceKey), sequenceNumber);
CFRelease(lvgProps);
CFDictionarySetValue(lvProps, key, value);
CFRelease(defaultLVProps);
return true;
error:
if (defaultLVProps) CFRelease(defaultLVProps);
return false;
}
static AppleLVMVolumeOnDisk *
buildLVMetaDataBlock(CFMutableDictionaryRef lvProps, CFDataRef extentData)
{
CFDataRef propData = 0;
AppleRAIDExtentOnDisk * extentList = (AppleRAIDExtentOnDisk *)CFDataGetBytePtr(extentData);
UInt64 extentCount = CFDataGetLength(extentData) / sizeof(AppleRAIDExtentOnDisk);
if (!extentCount || !extentList) return NULL;
AppleLVMVolumeOnDisk * lvData = calloc(1, kAppleLVMVolumeOnDiskMinSize);
if (!lvData) return NULL;
strlcpy(lvData->lvMagic, kAppleLVMVolumeMagic, sizeof(lvData->lvMagic));
lvData->lvHeaderSize = kAppleLVMVolumeOnDiskMinSize;
lvData->lvExtentsCount = extentCount;
CFMutableDictionaryRef cleanProps = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, lvProps);
if (!cleanProps) goto error;
CFIndex propCount = CFDictionaryGetCount(cleanProps);
if (!propCount) goto error;
const void ** keys = calloc(propCount, sizeof(void *));
if (!keys) goto error;
CFDictionaryGetKeysAndValues(cleanProps, keys, NULL);
UInt32 i;
for (i = 0; i < propCount; i++) {
if (!CFStringHasPrefix(keys[i], CFSTR("AppleLVM-"))) {
CFDictionaryRemoveValue(cleanProps, keys[i]);
}
}
CFDictionaryRemoveValue(cleanProps, CFSTR(kAppleLVMVolumeStatusKey));
propData = IOCFSerialize(cleanProps, kNilOptions);
if (!propData) {
IOLog1("AppleRAIDLib - serialize on logical data props failed\n");
goto error;
}
bcopy(CFDataGetBytePtr(propData), lvData->plist, CFDataGetLength(propData));
IOLog1("LogicalVolumeProps = %s\n", lvData->plist);
UInt32 firstExtent = CFDataGetLength(propData);
firstExtent = firstExtent + (UInt32)((char *)lvData->plist - (char *)lvData);
firstExtent = firstExtent + sizeof(AppleRAIDExtentOnDisk) - 1;
firstExtent = firstExtent / sizeof(AppleRAIDExtentOnDisk) * sizeof(AppleRAIDExtentOnDisk);
lvData->lvExtentsStart = firstExtent;
AppleRAIDExtentOnDisk * extent = (AppleRAIDExtentOnDisk *)((char *)lvData + firstExtent);
if (lvData->lvExtentsStart + sizeof(AppleRAIDExtentOnDisk) > lvData->lvHeaderSize) goto error;
for (i = 0; i < extentCount; i++) {
IOLog1(" %20llu - %12llu (%llu)\n",
extentList->extentByteOffset,
extentList->extentByteOffset + extentList->extentByteCount - 1,
extentList->extentByteCount);
*extent++ = *extentList++;
}
CFRelease(propData);
CFRelease(cleanProps);
return lvData;
error:
if (lvData) free(lvData);
if (propData) CFRelease(propData);
if (cleanProps) CFRelease(cleanProps);
return NULL;
}
AppleLVMVolumeRef
AppleLVMUpdateVolume(CFMutableDictionaryRef volProps)
{
CFStringRef volRef = (CFStringRef)CFDictionaryGetValue(volProps, CFSTR(kAppleLVMVolumeUUIDKey));
if (!volRef) return NULL;
CFDataRef extentData = (CFDataRef)CFDictionaryGetValue(volProps, CFSTR("_extent data_"));
if (extentData) {
CFRetain(extentData);
CFDictionaryRemoveValue(volProps, CFSTR("_extent data_"));
} else {
extentData = AppleLVMGetVolumeExtents(volRef);
if (!extentData) return NULL;
}
AppleLVMVolumeOnDisk * lvData = buildLVMetaDataBlock(volProps, extentData);
if (!lvData) goto error;
io_connect_t raidControllerPort = AppleRAIDOpenConnection();
if (!raidControllerPort) {
IOLog1("AppleLVMUpdateVolume - failed connecting to raid controller object?\n");
goto error;
}
kern_return_t kr;
char * buffer = (char *)lvData;
size_t bufferSize = kAppleLVMVolumeOnDiskMinSize;
char updateData[0x1000];
size_t updateDataSize = sizeof(updateData);
kr = IOConnectCallStructMethod(raidControllerPort, kAppleLVMUpdateLogicalVolume, buffer, bufferSize, updateData, &updateDataSize);
AppleRAIDCloseConnection();
if (kr != KERN_SUCCESS) {
IOLog1("AppleLVMUpdateVolume failed with %x calling client.\n", kr);
goto error;
}
CFRelease(extentData);
free(lvData);
CFRetain(volRef);
return volRef;
error:
if (extentData) CFRelease(extentData);
if (lvData) free(lvData);
return NULL;
}
bool
AppleLVMDestroyVolume(AppleLVMVolumeRef volRef)
{
kern_return_t kr;
size_t bufferSize = kAppleRAIDMaxUUIDStringSize;
char buffer[bufferSize];
char returnData[0x1000];
size_t returnDataSize = sizeof(returnData);
if (!CFStringGetCString(volRef, buffer, bufferSize, kCFStringEncodingUTF8)) {
IOLog1("AppleLVMDestroyVolume() CFStringGetCString failed?\n");
return NULL;
}
io_connect_t raidControllerPort = AppleRAIDOpenConnection();
if (!raidControllerPort) return NULL;
kr = IOConnectCallStructMethod(raidControllerPort, kAppleLVMDestroyLogicalVolume, buffer, bufferSize, returnData, &returnDataSize);
if (kr != KERN_SUCCESS) {
IOLog1("AppleLVMDestroyVolume failed with %x calling client.\n", kr);
}
AppleRAIDCloseConnection();
return (kr == KERN_SUCCESS);
}
UInt64
AppleLVMResizeVolume(CFMutableDictionaryRef lvProps, UInt64 newSize)
{
UInt64 currentSize = 0;
CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeSizeKey));
if (number) CFNumberGetValue(number, kCFNumberSInt64Type, ¤tSize);
if (!number || !currentSize) return 0;
if (!newSize) return currentSize;
if (currentSize == newSize) return 0;
CFStringRef volRef = (CFStringRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeUUIDKey));
if (!volRef) return 0;
AppleRAIDSetRef lvgRef = (CFStringRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMGroupUUIDKey));
if (!lvgRef) return false;
CFMutableDictionaryRef lvgProps = AppleRAIDGetSetProperties(lvgRef);
if (!lvgProps) return false;
if (newSize > currentSize) {
UInt64 freeSpace = 0;
number = (CFNumberRef)CFDictionaryGetValue(lvgProps, CFSTR(kAppleRAIDLVGFreeSpaceKey));
if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &freeSpace);
if (newSize > freeSpace) {
printf("AppleRAID: Insufficent free space to resize the logical volume.\n");
return 0;
}
}
CFDataRef originalExtentData;
originalExtentData = (CFDataRef)CFDictionaryGetValue(lvProps, CFSTR("_extent data_"));
if (!originalExtentData) {
originalExtentData = AppleLVMGetVolumeExtents(volRef);
if (!originalExtentData) return 0;
CFDictionarySetValue(lvProps, CFSTR("_extent data_"), originalExtentData);
CFRelease(originalExtentData);
}
CFMutableDataRef extentData = CFDataCreateMutableCopy(kCFAllocatorDefault, 0, originalExtentData);
CFDictionarySetValue(lvProps, CFSTR("_extent data_"), extentData);
CFRelease(extentData);
const void * sequenceNumber = CFDictionaryGetValue(lvgProps, CFSTR(kAppleRAIDSequenceNumberKey));
if (!sequenceNumber) return false;
CFDictionarySetValue(lvProps, CFSTR(kAppleLVMVolumeSequenceKey), sequenceNumber);
CFRelease(lvgProps);
if (newSize < currentSize) {
UInt64 newExtentCount = truncateExtents(extentData, newSize);
if (!newExtentCount) return 0;
number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &newSize);
CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeSizeKey), number);
number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &newExtentCount);
CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeExtentCountKey), number);
return newSize;
}
AppleRAIDSetRef setRef = (CFStringRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMGroupUUIDKey));
if (!setRef) return 0;
setInfo_t * lvgInfo = getSetInfo(setRef);
if (!lvgInfo) return 0;
UInt64 lvgExtentCount = 0;
number = (CFNumberRef)CFDictionaryGetValue(lvgInfo->setProps, CFSTR(kAppleRAIDLVGExtentsKey));
if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &lvgExtentCount);
AppleRAIDExtentOnDisk * lvgExtentList = getVolumeExtents(setRef, &lvgExtentCount);
if (!lvgExtentList) return 0;
CFStringRef volumeLocation = (CFStringRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeLocationKey));
if (!volumeLocation) return 0;
UInt64 size = growLastExtent(extentData, lvgExtentList, lvgExtentCount, newSize);
if (size) {
number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &newSize);
CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeSizeKey), number);
if (size == newSize) return newSize;
currentSize = size;
}
UInt64 extentCount = 0;
AppleRAIDExtentOnDisk * extentList = allocateExtent(lvgExtentList, lvgExtentCount, newSize - currentSize, volumeLocation, &extentCount);
if (!extentList) return 0;
CFDataAppendBytes(extentData, (const UInt8 *)extentList, extentCount * sizeof(AppleRAIDExtentOnDisk));
free(extentList);
number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &newSize);
CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeSizeKey), number);
UInt64 newExtentCount = 0;
number = (CFNumberRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeExtentCountKey));
if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &newExtentCount);
newExtentCount += extentCount;
number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &newExtentCount);
if (number) CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeExtentCountKey), number);
if (number) CFRelease(number);
return newSize;
}
CFMutableDictionaryRef
AppleLVMSnapShotVolume(CFMutableDictionaryRef lvProps, CFStringRef snapType, UInt64 snapSize)
{
UInt64 lvSize = 0;
CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeSizeKey));
if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &lvSize);
if (!number || !lvSize) return NULL;
snapSize = MIN(snapSize, lvSize);
UInt64 bitmapSize = calculateBitMapSize(lvSize, 0, NULL);
CFStringRef lvgUUID = (CFStringRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMGroupUUIDKey));
if (!lvgUUID) return NULL;
CFStringRef lvUUID = (CFStringRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeUUIDKey));
if (!lvgUUID) return NULL;
CFStringRef location = (CFStringRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeLocationKey));
if (!lvgUUID) return NULL;
CFMutableDictionaryRef bitmap = AppleLVMCreateVolume(lvgUUID, CFSTR(kAppleLVMVolumeTypeBitMap), bitmapSize, CFSTR(kAppleLVMVolumeLocationFast));
if (!bitmap) return NULL;
CFDictionarySetValue(bitmap, CFSTR(kAppleLVMParentUUIDKey), lvUUID);
CFStringRef bitmapUUID = AppleLVMUpdateVolume(bitmap);
if (!bitmapUUID) return NULL;
CFMutableDictionaryRef snap = AppleLVMCreateVolume(lvgUUID, snapType, snapSize, location);
if (!snap) return NULL;
CFDictionarySetValue(snap, CFSTR(kAppleLVMParentUUIDKey), lvUUID);
CFRelease(bitmap);
return snap;
}
bool
AppleLVMMigrateVolume(AppleLVMVolumeRef volRef, AppleRAIDMemberRef toRef, CFStringRef volumeLocation)
{
return false;
}
AppleLVMVolumeRef
AppleLVMRemoveMember(AppleLVMVolumeRef volRef, AppleRAIDMemberRef memberRef)
{
return NULL;
}
AppleLVMVolumeRef
AppleLVMMergeGroups(AppleRAIDSetRef setRef, AppleRAIDSetRef donorSetRef)
{
return NULL;
}