#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/fcntl.h>
#include <mach/mach.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFUnserialize.h>
#include <IOKit/IOMessage.h>
#include <MediaKit/MKMedia.h>
#include <MediaKit/MKMediaAccess.h>
#include "AppleRAIDUserLib.h"
#include "AppleRAIDMember.h" // for V2 header
#define AUTO_YES 1
#define AUTO_NO 2
static int autoRebuild = 0;
static UInt64 blockSize = 0;
static bool extents = false;
static char * hint = 0;
static char * nickname = 0;
static int quickRebuild = 0;
static UInt64 timeout = 0;
static bool verbose = false;
static UInt64 volSize = 0;
static void
usage()
{
printf("\n");
printf("usage:\n");
printf("\n");
printf("artest --list\n");
printf("artest --lvlist [--extents] <lv uuid | lvg uuid>\n");
printf("\n");
printf("artest --create --name <nickname> --level <level> <options> disk1s3 disk2s3 disk3s3 ...\n");
printf("artest --destroy <set uuid>\n");
printf("\n");
printf("artest --add <set uuid> disk1s3 ...\n");
printf("artest --spare <set uuid> disk1s3 ...\n");
printf("artest --remove <set uuid> <member uuid> ...\n");
printf("artest --modify <set uuid> <options>\n");
printf("\n");
printf("artest --lvcreate <lvg uuid> <lvoptions>\n");
printf("artest --lvdestroy <lv uuid>\n");
printf("\n");
printf("artest --lvmodify <lv uuid> <lvoptions>\n");
printf("artest --lvresize <lv uuid> [--size <number>]\n");
printf("artest --lvsnap <lv uuid> [--size <number>] --level=\"snap ro\"|\"snap rw\">\n");
printf("\n");
printf("artest --erase disk1s3 disk2s3 disk3s3 ...\n");
printf("artest --header disk1s3 disk2s3 disk3s3 ...\n");
printf("\n");
printf("parameters:\n");
printf(" <uuid> = XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\n");
printf(" <nickname> = \"the volume name\"\n");
printf(" <level> = stripe, mirror, concat, lvg\n");
printf(" <hint> = Apple_HFS, RAIDNoMedia, RAIDNoFS\n");
printf(" <options> = [--auto-rebuild=Yes,No] [--block-size=0x8000] [--hint=<hint>]\n");
printf(" <options> = [--name=<nickname>] [--timeout=30] [--quick-rebuild=Yes]\n");
printf(" <lvoptions> = [--level=concat] [--hint=<hint>] [--name=<nickname>] [--size 0xXXXXXXXXXXXXXXXX]\n");
printf("\n");
printf("global options:\n");
printf(" --verbose\n");
printf(" --watch (also a command)\n");
}
static void
CFPrintf(CFStringRef format, ...)
{
CFStringRef cfstring;
va_list argList;
va_start(argList, format);
cfstring = CFStringCreateWithFormatAndArguments(NULL, NULL, format, argList);
va_end(argList);
CFIndex cfstringSize = CFStringGetLength(cfstring);
CFIndex stringSize = CFStringGetMaximumSizeForEncoding(cfstringSize, kCFStringEncodingUTF8) + 1;
char *string = malloc(stringSize);
if (CFStringGetCString(cfstring, string, stringSize, kCFStringEncodingUTF8)) {
printf("%s", string);
}
free(string);
}
#define PMROptions (PMEXTENDEDMODE | PMSECTORIZE | PMSORTMAP)
#define kRAID_ONLINE "Apple_RAID"
#define kRAID_OFFLINE "Apple_RAID_Offline"
static bool
switchPartition(char * diskName, char * partitionType)
{
char wholeDevicePath[256];
sprintf(wholeDevicePath, "/dev/%s", diskName);
unsigned int partitionNumber = 0;
char * c = wholeDevicePath + 5 + 4; while (*c != 's' && *c++); if (*c == 's') {
*c = 0; sscanf(c+1, "%u", &partitionNumber); }
if (!partitionNumber) return true;
#define LIVERAID
#ifdef LIVERAID
char * optionString = "<dict> <key>Writable</key> <true/> <key>Shared Writer</key> <true/></dict>";
#else
char * optionString = "<dict> <key>Writable</key> <true/> </dict>";
#endif
CFDictionaryRef options = IOCFUnserialize(optionString, kCFAllocatorDefault, 0, NULL);
if (!options) exit(1);
int32_t err;
MKMediaRef device = MKMediaCreateWithPath(nil, wholeDevicePath, options, &err);
CFRelease(options);
if (!device || err) return false;
options = NULL;
MKStatus err2;
CFMutableDictionaryRef media = MKCFReadMedia(options, device, &err2);
if (!media || err2) goto Failure;
CFMutableArrayRef Schemes = (CFMutableArrayRef) CFDictionaryGetValue(media, CFSTR("Schemes"));
if (!Schemes) goto Failure;
CFMutableDictionaryRef Scheme = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(Schemes, 0);
if (!Scheme) goto Failure;
CFMutableArrayRef Sections = (CFMutableArrayRef) CFDictionaryGetValue(Scheme, CFSTR("Sections"));
if (!Sections) goto Failure;
CFMutableDictionaryRef Section = (CFMutableDictionaryRef) CFArrayDictionarySearch(Sections, CFSTR("ID"), CFSTR("MAP"));
if (!Section) goto Failure;
CFMutableArrayRef Partitions = (CFMutableArrayRef) CFDictionaryGetValue(Section, CFSTR("Partitions"));
if (!Partitions) goto Failure;
CFNumberRef partitionIndex = CFNumberCreate(nil, kCFNumberSInt32Type, &partitionNumber);
if (!partitionIndex) goto Failure;
CFMutableDictionaryRef Partition = (CFMutableDictionaryRef) CFArrayDictionarySearch(Partitions, CFSTR("Partition ID"), partitionIndex);
if (!Partition) goto Failure;
CFStringRef Type = CFStringCreateWithCString(nil, partitionType, kCFStringEncodingUTF8);
if (!Type) goto Failure;
CFDictionarySetValue(Partition, CFSTR("Type"), Type);
CFMutableDictionaryRef woptions = CFDictionaryCreateMutable(kCFAllocatorDefault,
2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!woptions) goto Failure;
CFDictionarySetValue(woptions, CFSTR("Retain existing content"), kCFBooleanTrue);
CFDictionarySetValue(woptions, CFSTR("Direct Mode"), kCFBooleanTrue);
err2 = MKCFWriteMedia(media, nil, nil, woptions, device);
MKCFDisposeMedia(media);
CFRelease(woptions);
CFRelease(device);
return !err2;
Failure:
if (media) MKCFDisposeMedia(media);
if (device) CFRelease(device);
return false;
}
static void
addMember(char * uuid, CFStringRef type, int argc, char* argv[])
{
if (argc < 1) {
usage();
exit(1);
}
CFStringRef setUUID = CFStringCreateWithCString(kCFAllocatorDefault, uuid, kCFStringEncodingUTF8);
if (!setUUID) exit(1);
CFMutableDictionaryRef setInfo = AppleRAIDGetSetProperties(setUUID);
if (!setInfo) {
printf("addMember - failed to find RAID set \"%s\"\n", uuid);
exit(1);
}
int partitionCount = argc;
char **firstPartition = argv;
while (argc--) {
printf("adding partition \"%s\"\n", *argv);
CFStringRef partitionName = CFStringCreateWithCString(kCFAllocatorDefault, *argv, kCFStringEncodingUTF8);
bool success = switchPartition(*argv, kRAID_OFFLINE);
if (!success) {
printf("switching the partition on \"%s\" to %s FAILED.\n", *argv, kRAID_OFFLINE);
exit(1);
}
AppleRAIDMemberRef member = AppleRAIDAddMember(setInfo, partitionName, type);
if (!member) {
printf("addMember - there was problem adding partition \"%s\"\n", *argv);
exit(1);
}
argv++;
}
printf("updating set \"%s\".\n", uuid);
AppleRAIDSetRef set = AppleRAIDUpdateSet(setInfo);
CFRelease(setInfo);
if (!set) {
printf("something went wrong adding members to the set\n");
exit(1);
}
while (partitionCount--) {
printf("switching the partition on \"%s\" to %s.\n", *firstPartition, kRAID_ONLINE);
bool success = switchPartition(*firstPartition, kRAID_ONLINE);
if (!success) {
printf("switching the partition on \"%s\" to %s FAILED.\n", *firstPartition, kRAID_ONLINE);
exit(1);
}
firstPartition++;
}
}
static void
createSet(char * levelCString, char * nameCString, int argc, char* argv[])
{
if (!levelCString || !nameCString || argc < 2) {
usage();
exit(1);
}
CFStringRef level = CFStringCreateWithCString(kCFAllocatorDefault, levelCString, kCFStringEncodingUTF8);
CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, nameCString, kCFStringEncodingUTF8);
if (!level || !name) exit(1);
CFMutableArrayRef descriptionArray = AppleRAIDGetSetDescriptions();
CFIndex dCount = CFArrayGetCount(descriptionArray);
CFIndex dIndex = 0;
CFStringRef dLevel = 0;
for (dIndex = 0; dIndex < dCount; dIndex++) {
CFMutableDictionaryRef setDescription = (CFMutableDictionaryRef)CFArrayGetValueAtIndex(descriptionArray, dIndex);
dLevel = (CFStringRef)CFDictionaryGetValue(setDescription, CFSTR(kAppleRAIDLevelNameKey));
if (!dLevel) break;
if (CFStringCompare(dLevel, level, kCFCompareCaseInsensitive) == kCFCompareEqualTo) break;
dLevel = 0;
}
if (dLevel == 0) {
printf("raid level \"%s\" is not valid?.\n", levelCString);
exit(1);
}
CFMutableDictionaryRef setInfo = AppleRAIDCreateSet(dLevel, name);
if (!setInfo) exit(1);
char **firstPartition = argv;
int partitionCount = argc;
while (argc--) {
printf("adding partition \"%s\"\n", *argv);
CFStringRef partitionName = CFStringCreateWithCString(kCFAllocatorDefault, *argv, kCFStringEncodingUTF8);
AppleRAIDMemberRef member = AppleRAIDAddMember(setInfo, partitionName, CFSTR(kAppleRAIDMembersKey));
if (!member) {
printf("there was problem adding partition \"%s\"\n", *argv);
exit(1);
}
CFShow(member);
bool success = switchPartition(*argv, kRAID_OFFLINE);
if (!success) {
printf("switching the partition on \"%s\" to %s FAILED.\n", *argv, kRAID_OFFLINE);
exit(1);
}
argv++;
}
if (autoRebuild == AUTO_YES) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetAutoRebuildKey), (void *)kCFBooleanTrue);
if (quickRebuild == AUTO_YES) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetQuickRebuildKey), (void *)kCFBooleanTrue);
if (blockSize) {
CFNumberRef blockSizeCF = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &blockSize);
if (blockSizeCF) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDChunkSizeKey), (void *)blockSizeCF);
}
if (hint) {
CFStringRef hintCF = CFStringCreateWithCString(kCFAllocatorDefault, hint, kCFStringEncodingUTF8);
if (hintCF) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetContentHintKey), (void *)hintCF);
}
if (timeout) {
CFNumberRef timeoutCF = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &timeout);
if (timeoutCF) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetTimeoutKey), (void *)timeoutCF);
}
printf("creating the set \"%s\".\n", nameCString);
AppleRAIDSetRef set = AppleRAIDUpdateSet(setInfo);
CFPrintf(CFSTR("created set %@\n"), set);
CFRelease(setInfo);
if (!set) {
printf("something went wrong creating the set\n");
exit(0);
}
while (partitionCount--) {
printf("switching the partition on \"%s\" to %s.\n", *firstPartition, kRAID_ONLINE);
bool success = switchPartition(*firstPartition, kRAID_ONLINE);
if (!success) {
printf("switching the partition on \"%s\" to %s FAILED.\n", *firstPartition, kRAID_ONLINE);
exit(1);
}
firstPartition++;
}
}
static void
destroySet(char * nameCString, int argc, char* argv[])
{
if (!nameCString || argc) {
usage();
exit(1);
}
CFStringRef setUUID = CFStringCreateWithCString(kCFAllocatorDefault, nameCString, kCFStringEncodingUTF8);
if (!setUUID) exit(1);
bool success = AppleRAIDDestroySet(setUUID);
if (!success) {
printf("there was a problem destroying the set %s.\n", nameCString);
}
}
static void
erasePartition(int argc, char* argv[])
{
if (argc < 1) {
usage();
exit(1);
}
while (argc--) {
printf("switching the partition type on \"%s\" to %s.\n", *argv, kRAID_OFFLINE);
bool success = switchPartition(*argv, kRAID_OFFLINE);
if (!success) {
printf("switching partition type FAILED.\n");
}
printf("erasing raid headers on partition \"%s\"\n", *argv);
CFStringRef partitionName = CFStringCreateWithCString(kCFAllocatorDefault, *argv, kCFStringEncodingUTF8);
success = AppleRAIDRemoveHeaders(partitionName);
if (!success) {
printf("erasing the raid headers on partition \"%s\" FAILED.\n", *argv);
}
argv++;
}
}
static void
dumpHeader(int argc, char* argv[])
{
if (argc < 1) {
usage();
exit(1);
}
while (argc--) {
CFStringRef partitionName = CFStringCreateWithCString(kCFAllocatorDefault, *argv, kCFStringEncodingUTF8);
CFDataRef data = AppleRAIDDumpHeader(partitionName);
if (data) {
AppleRAIDHeaderV2 * header = (AppleRAIDHeaderV2 *)CFDataGetBytePtr(data);
if (header) printf("%s\n", header->plist);
}
argv++;
}
}
static void
dumpSetProperties(CFMutableDictionaryRef set)
{
CFPrintf(CFSTR("\n%@\n"), CFDictionaryGetValue(set, CFSTR(kAppleRAIDSetUUIDKey)));
CFPrintf(CFSTR("\t\"%@\" type = %@ /dev/%@\n"),
CFDictionaryGetValue(set, CFSTR(kAppleRAIDSetNameKey)),
CFDictionaryGetValue(set, CFSTR(kAppleRAIDLevelNameKey)),
CFDictionaryGetValue(set, CFSTR("BSD Name")));
CFPrintf(CFSTR("\tstatus = %@, sequence = %@\n"),
CFDictionaryGetValue(set, CFSTR(kAppleRAIDStatusKey)),
CFDictionaryGetValue(set, CFSTR(kAppleRAIDSequenceNumberKey)));
CFPrintf(CFSTR("\tchunk count = %@, chunk size = %@\n"),
CFDictionaryGetValue(set, CFSTR(kAppleRAIDChunkCountKey)),
CFDictionaryGetValue(set, CFSTR(kAppleRAIDChunkSizeKey)));
CFStringRef level = CFDictionaryGetValue(set, CFSTR(kAppleRAIDLevelNameKey));
if (CFStringCompare(level, CFSTR("mirror"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
CFPrintf(CFSTR("\tcontent hint = %@, auto = %@, quick = %@, timeout = %@\n"),
CFDictionaryGetValue(set, CFSTR(kAppleRAIDSetContentHintKey)),
CFDictionaryGetValue(set, CFSTR(kAppleRAIDSetAutoRebuildKey)),
CFDictionaryGetValue(set, CFSTR(kAppleRAIDSetQuickRebuildKey)),
CFDictionaryGetValue(set, CFSTR(kAppleRAIDSetTimeoutKey)));
} else if (CFStringCompare(level, CFSTR("LVG"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
CFPrintf(CFSTR("\tcontent hint = %@, lv count = %@, free space %@\n"),
CFDictionaryGetValue(set, CFSTR(kAppleRAIDSetContentHintKey)),
CFDictionaryGetValue(set, CFSTR(kAppleRAIDLVGVolumeCountKey)),
CFDictionaryGetValue(set, CFSTR(kAppleRAIDLVGFreeSpaceKey)));
} else {
CFPrintf(CFSTR("\tcontent hint = %@\n"), CFDictionaryGetValue(set, CFSTR(kAppleRAIDSetContentHintKey)));
}
if (verbose) CFShow(set);
}
static void
dumpMemberProperties(CFMutableDictionaryRef member)
{
CFPrintf(CFSTR("\t%@\n"), CFDictionaryGetValue(member, CFSTR(kAppleRAIDMemberUUIDKey)));
CFPrintf(CFSTR("\t\tmember index = %@, sequence = %@, /dev/%@\n"),
CFDictionaryGetValue(member, CFSTR(kAppleRAIDMemberIndexKey)),
CFDictionaryGetValue(member, CFSTR(kAppleRAIDSequenceNumberKey)),
CFDictionaryGetValue(member, CFSTR("BSD Name")));
CFPrintf(CFSTR("\t\tstatus = %@, chunk count = %@, rebuild = %@\n"),
CFDictionaryGetValue(member, CFSTR(kAppleRAIDMemberStatusKey)),
CFDictionaryGetValue(member, CFSTR(kAppleRAIDChunkCountKey)),
CFDictionaryGetValue(member, CFSTR(kAppleRAIDRebuildStatus)));
if (verbose) CFShow(member);
}
static void
listRAIDSets()
{
UInt32 filter = kAppleRAIDAllSets;
CFMutableArrayRef theList = AppleRAIDGetListOfSets(filter);
CFIndex setCount = theList ? CFArrayGetCount(theList) : 0;
printf("AppleRAIDGetListOfSets found %d sets\n", (int)setCount); fflush(stdout);
CFIndex i;
for (i=0; i < setCount; i++) {
CFStringRef setName = (CFStringRef)CFArrayGetValueAtIndex(theList, i);
if (setName) {
CFMutableDictionaryRef setProp = AppleRAIDGetSetProperties(setName);
if (!setProp) {
CFPrintf(CFSTR("%@\n\t(lookup failed)\n"), setName);
continue;
}
dumpSetProperties(setProp);
bool spares = false;
CFMutableArrayRef members = (CFMutableArrayRef)CFDictionaryGetValue(setProp, CFSTR(kAppleRAIDMembersKey));
CFIndex j, memberCount = members ? CFArrayGetCount(members) : 0;
printf("members (%d):\n", (int)memberCount);
again:
for (j=0; j < memberCount; j++) {
CFStringRef memberName = (CFStringRef)CFArrayGetValueAtIndex(members, j);
if (memberName) {
CFMutableDictionaryRef memberProp = AppleRAIDGetMemberProperties(memberName);
if (memberProp) {
dumpMemberProperties(memberProp);
CFRelease(memberProp);
} else {
CFPrintf(CFSTR("\t%@\n\t\t(lookup failed)\n"), memberName);
}
}
}
if (!spares) {
members = (CFMutableArrayRef)CFDictionaryGetValue(setProp, CFSTR(kAppleRAIDSparesKey));
memberCount = members ? CFArrayGetCount(members) : 0;
if (memberCount) printf("spares: (%d)\n", (int)memberCount);
spares = true;
goto again;
}
spares = false;
CFRelease(setProp);
}
}
if (theList) CFRelease(theList);
}
static void
modifySet(char * setUUIDCString, int argc, char* argv[])
{
if (!setUUIDCString || argc) {
usage();
exit(1);
}
CFStringRef setUUID = CFStringCreateWithCString(kCFAllocatorDefault, setUUIDCString, kCFStringEncodingUTF8);
if (!setUUID) exit(1);
CFMutableDictionaryRef setInfo = AppleRAIDGetSetProperties(setUUID);
if (!setInfo) {
printf("modifySet - failed to find RAID set \"%s\"\n", setUUIDCString);
exit(1);
}
if (autoRebuild == AUTO_YES) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetAutoRebuildKey), (void *)kCFBooleanTrue);
if (autoRebuild == AUTO_NO) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetAutoRebuildKey), (void *)kCFBooleanFalse);
if (quickRebuild == AUTO_YES) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetQuickRebuildKey), (void *)kCFBooleanTrue);
if (quickRebuild == AUTO_NO) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetQuickRebuildKey), (void *)kCFBooleanFalse);
if (blockSize) {
CFNumberRef blockSizeCF = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &blockSize);
if (blockSizeCF) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDChunkSizeKey), (void *)blockSizeCF);
}
if (hint) {
CFStringRef hintCF = CFStringCreateWithCString(kCFAllocatorDefault, hint, kCFStringEncodingUTF8);
if (hintCF) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetContentHintKey), (void *)hintCF);
}
if (timeout) {
CFNumberRef timeoutCF = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &timeout);
if (timeoutCF) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetTimeoutKey), (void *)timeoutCF);
}
if (nickname) {
CFStringRef nicknameCF = CFStringCreateWithCString(kCFAllocatorDefault, nickname, kCFStringEncodingUTF8);
if (nicknameCF) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetNameKey), (void *)nicknameCF);
}
printf("modifying the set \"%s\".\n", setUUIDCString);
AppleRAIDSetRef set = AppleRAIDUpdateSet(setInfo);
if (!set) printf("something went wrong updating the set\n");
if (set) CFRelease(set);
CFRelease(setInfo);
CFRelease(setUUID);
}
static void
removeMember(char * setUUIDCString, int argc, char* argv[])
{
if (!setUUIDCString || argc < 1) {
usage();
exit(1);
}
CFStringRef setUUID = CFStringCreateWithCString(kCFAllocatorDefault, setUUIDCString, kCFStringEncodingUTF8);
if (!setUUID) exit(1);
CFMutableDictionaryRef setInfo = AppleRAIDGetSetProperties(setUUID);
if (!setInfo) {
printf("removeMember - failed to find RAID set \"%s\"\n", setUUIDCString);
exit(1);
}
int partitionCount = argc;
char **firstPartition = argv;
while (argc--) {
printf("removing member \"%s\"\n", *argv);
CFStringRef memberUUID = CFStringCreateWithCString(kCFAllocatorDefault, *argv, kCFStringEncodingUTF8);
bool success = AppleRAIDRemoveMember(setInfo, memberUUID);
if (!success) {
printf("there was problem removing the member \"%s\"\n", *argv);
}
CFRelease(memberUUID);
argv++;
}
AppleRAIDSetRef set = AppleRAIDUpdateSet(setInfo);
if (!set) printf("something went wrong updating the set\n");
if (set) CFRelease(set);
CFRelease(setInfo);
CFRelease(setUUID);
while (partitionCount--) {
printf("switching the partition on \"%s\" to %s.\n", *firstPartition, kRAID_OFFLINE);
bool success = switchPartition(*firstPartition, kRAID_OFFLINE);
if (!success) {
printf("switching the partition on \"%s\" to %s FAILED.\n", *firstPartition, kRAID_OFFLINE);
}
firstPartition++;
}
}
static void
createLogicalVolume(char * nameCString, char * volTypeCString, int argc, char* argv[])
{
if (!nameCString || argc) {
usage();
exit(1);
}
CFStringRef lvgUUID = CFStringCreateWithCString(kCFAllocatorDefault, nameCString, kCFStringEncodingUTF8);
if (!lvgUUID) exit(1);
if (!volSize) volSize = 0x40000000;
CFStringRef volType = 0;
if (volTypeCString) {
volType = CFStringCreateWithCString(kCFAllocatorDefault, volTypeCString, kCFStringEncodingUTF8);
} else {
volType = CFSTR(kAppleLVMVolumeTypeConcat);
}
if (!volType) exit(1);
CFMutableDictionaryRef lvDict = AppleLVMCreateVolume(lvgUUID, volType, volSize, CFSTR(kAppleLVMVolumeLocationFast));
if (!lvDict) {
printf("there was a problem allocating/setting up the logical volume\n");
exit(1);
}
if (nickname) {
CFStringRef nameCF = CFStringCreateWithCString(kCFAllocatorDefault, nickname, kCFStringEncodingUTF8);
if (nameCF) AppleLVMModifyVolume(lvDict, CFSTR(kAppleLVMVolumeNameKey), (void *)nameCF);
}
if (hint) {
CFStringRef hintCF = CFStringCreateWithCString(kCFAllocatorDefault, hint, kCFStringEncodingUTF8);
if (hintCF) AppleLVMModifyVolume(lvDict, CFSTR(kAppleLVMVolumeContentHintKey), (void *)hintCF);
}
AppleLVMVolumeRef volRef = AppleLVMUpdateVolume(lvDict);
if (!volRef) {
printf("there was a problem writing out the logical volume onto the group %s.\n", nameCString);
exit(2);
}
}
static void
destroyLogicalVolume(char * nameCString, int argc, char* argv[])
{
if (!nameCString || argc) {
usage();
exit(1);
}
CFStringRef lvUUID = CFStringCreateWithCString(kCFAllocatorDefault, nameCString, kCFStringEncodingUTF8);
if (!lvUUID) exit(1);
bool success = AppleLVMDestroyVolume(lvUUID);
if (!success) {
printf("there was a problem destroying the logical volume %s.\n", nameCString);
}
}
static void
dumpLogicalVolumeExtents(CFMutableDictionaryRef lv)
{
CFStringRef lvUUID = CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeUUIDKey));
if (!lvUUID) { printf("\ninternal error, no uuid in lv dict\n"); return; };
CFDataRef extentData = (CFDataRef)AppleLVMGetVolumeExtents(lvUUID);
if (!extentData) { printf("\nno extent data found?\n"); return; };
AppleRAIDExtentOnDisk * extentList = (AppleRAIDExtentOnDisk *)CFDataGetBytePtr(extentData);
UInt64 extentCount = CFDataGetLength(extentData) / sizeof(AppleRAIDExtentOnDisk);
if (!extentCount || !extentList) { printf("\nextent data empty?\n"); return; };
printf("\textent list:\n");
UInt32 i;
for (i = 0; i < extentCount; i++) {
printf(" %20llu - %12llu (%llu)\n",
extentList[i].extentByteOffset,
extentList[i].extentByteOffset + extentList[i].extentByteCount - 1,
extentList[i].extentByteCount);
}
}
static void
dumpLogicalVolumeProperties(CFMutableDictionaryRef lv)
{
CFPrintf(CFSTR("\n%@\n"), CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeUUIDKey)));
CFPrintf(CFSTR("\t\"%@\" type = %@ /dev/%@\n"),
CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeNameKey)),
CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeTypeKey)),
CFDictionaryGetValue(lv, CFSTR("BSD Name")));
CFPrintf(CFSTR("\tbyte size = %@, content hint = %@\n"),
CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeSizeKey)),
CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeContentHintKey)));
CFPrintf(CFSTR("\tstatus = %@, sequence = %@ extent count = %@\n"),
CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeStatusKey)),
CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeSequenceKey)),
CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeExtentCountKey)));
CFStringRef parent = CFDictionaryGetValue(lv, CFSTR(kAppleLVMParentUUIDKey));
if (parent) CFPrintf(CFSTR("\tparent = %@\n"), parent);
if (extents) dumpLogicalVolumeExtents(lv);
if (verbose) CFShow(lv);
}
static void listLogicalVolumes(char * nameCString, int argc, char* argv[]);
static void
listAllLogicalVolumes()
{
UInt32 filter = kAppleRAIDAllSets;
CFMutableArrayRef theList = AppleRAIDGetListOfSets(filter);
CFIndex setCount = theList ? CFArrayGetCount(theList) : 0;
CFIndex i;
for (i=0; i < setCount; i++) {
CFStringRef setName = (CFStringRef)CFArrayGetValueAtIndex(theList, i);
if (setName) {
CFMutableDictionaryRef setProp = AppleRAIDGetSetProperties(setName);
if (!setProp) continue;
CFStringRef level = CFDictionaryGetValue(setProp, CFSTR(kAppleRAIDLevelNameKey));
if (!level) continue;
if (CFStringCompare(level, CFSTR("LVG"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
dumpSetProperties(setProp);
char uuid[256];
if (CFStringGetCString(setName, uuid, 256, kCFStringEncodingUTF8)) {
listLogicalVolumes(uuid, 0, NULL);
}
}
CFRelease(setProp);
}
}
if (theList) CFRelease(theList);
}
static void
listLogicalVolumes(char * nameCString, int argc, char* argv[])
{
if (!nameCString && !argc) {
listAllLogicalVolumes();
exit(0);
}
if (!nameCString && argc == 1) nameCString = argv[0]; if (!nameCString) {
usage();
exit(1);
}
CFStringRef lvUUID = CFStringCreateWithCString(kCFAllocatorDefault, nameCString, kCFStringEncodingUTF8);
if (!lvUUID) exit(1);
CFMutableDictionaryRef props = AppleLVMGetVolumeProperties(lvUUID);
if (props) {
dumpLogicalVolumeProperties(props);
CFRelease(props);
return;
}
CFMutableArrayRef volumes = AppleLVMGetVolumesForGroup(lvUUID, NULL);
if (!volumes) {
printf("there was a problem finding the logical volume/group %s.\n", nameCString);
exit(1);
}
int count = CFArrayGetCount(volumes);
if (count == 0) {
printf("\n%s contains no logical volumes.\n", nameCString);
return;
}
int i;
for (i = 0; i < count; i++) {
CFStringRef UUID = (CFStringRef)CFArrayGetValueAtIndex(volumes, i);
CFMutableDictionaryRef props = AppleLVMGetVolumeProperties(UUID);
if (props) {
dumpLogicalVolumeProperties(props);
CFRelease(props);
}
}
printf("\n");
}
static void
modifyLogicalVolume(char * nameCString, int argc, char* argv[])
{
if (!nameCString || argc) {
usage();
exit(1);
}
CFStringRef lvUUID = CFStringCreateWithCString(kCFAllocatorDefault, nameCString, kCFStringEncodingUTF8);
if (!lvUUID) exit(1);
CFMutableDictionaryRef lvDict = AppleLVMGetVolumeProperties(lvUUID);
if (!lvDict) {
printf("there was a problem finding the logical volume %s.\n", nameCString);
exit(1);
}
if (nickname) {
CFStringRef nameCF = CFStringCreateWithCString(kCFAllocatorDefault, nickname, kCFStringEncodingUTF8);
if (nameCF) AppleLVMModifyVolume(lvDict, CFSTR(kAppleLVMVolumeNameKey), (void *)nameCF);
}
if (hint) {
CFStringRef hintCF = CFStringCreateWithCString(kCFAllocatorDefault, hint, kCFStringEncodingUTF8);
if (hintCF) AppleLVMModifyVolume(lvDict, CFSTR(kAppleLVMVolumeContentHintKey), (void *)hintCF);
}
AppleLVMVolumeRef volRef = AppleLVMUpdateVolume(lvDict);
if (!volRef) {
printf("there was a problem updating the logical volume %s.\n", nameCString);
exit(2);
}
}
static void
resizeLogicalVolume(char * nameCString, int argc, char* argv[])
{
if (!nameCString || argc) {
usage();
exit(1);
}
CFStringRef lvUUID = CFStringCreateWithCString(kCFAllocatorDefault, nameCString, kCFStringEncodingUTF8);
if (!lvUUID) exit(1);
CFMutableDictionaryRef lvProps = AppleLVMGetVolumeProperties(lvUUID);
if (!lvProps) {
printf("there was a problem finding the logical volume %s.\n", nameCString);
exit(1);
}
if (!volSize) {
CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeSizeKey));
if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &volSize);
volSize += 0x40000000;
}
UInt64 newSize = AppleLVMResizeVolume(lvProps, volSize);
if (!newSize) {
printf("there was a problem resizing the logical volume %s.\n", nameCString);
exit(1);
}
AppleLVMVolumeRef volRef = AppleLVMUpdateVolume(lvProps);
if (!volRef) {
printf("there was a problem updating the logical volume %s.\n", nameCString);
exit(2);
}
printf("the logical volume %s has been resized to 0x%lld.\n", nameCString, newSize);
}
static void
snapshotLogicalVolume(char * nameCString, char * snapType, int argc, char* argv[])
{
if (!nameCString || argc) {
usage();
exit(1);
}
CFStringRef lvUUID = CFStringCreateWithCString(kCFAllocatorDefault, nameCString, kCFStringEncodingUTF8);
if (!lvUUID) exit(1);
CFMutableDictionaryRef lvProps = AppleLVMGetVolumeProperties(lvUUID);
if (!lvProps) {
printf("there was a problem finding the logical volume %s.\n", nameCString);
exit(1);
}
CFStringRef lvType = 0;
if (snapType) {
lvType = CFStringCreateWithCString(kCFAllocatorDefault, snapType, kCFStringEncodingUTF8);
} else {
lvType = CFSTR(kAppleLVMVolumeTypeSnapRO);
}
if (!lvType) exit(1);
UInt64 percent = 0;
if (volSize < 100) percent = volSize;
if (volSize <= 100) volSize = 0;
if (!volSize) {
CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeSizeKey));
if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &volSize);
}
if (percent) volSize = volSize * percent / (UInt64)100;
CFMutableDictionaryRef snapProps = AppleLVMSnapShotVolume(lvProps, lvType, volSize);
if (!snapProps) {
printf("there was a problem initializing the snapshot for %s.\n", nameCString);
exit(1);
}
AppleLVMVolumeRef snapRef = AppleLVMUpdateVolume(snapProps);
if (!snapRef) {
printf("there was a problem updating the snapshot volume.\n");
exit(2);
}
printf("the logical volume %s now has a snapshot.\n", nameCString);
}
static void
callBack(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
char setName[128];
if (!CFStringGetCString(object, setName, 128, kCFStringEncodingUTF8)) bcopy("bogus set name?", setName, 128);
char event[128];
if (!CFStringGetCString(name, event, 128, kCFStringEncodingUTF8)) bcopy("bogus event string?", event, 128);
printf("Notification for %s, event = %s.\n", setName, event); fflush(stdout);
}
static void signalHandler(int sigraised);
int
main(int argc, char* argv[])
{
bool add = false, create = false, destroy = false, erase = false, header = false;
bool list = false, lvcreate = false, lvdestroy = false, lvlist = false, lvmodify = false, lvresize = false, lvsnap = false;
bool modify = false, remove = false, spare = false, watch = false;
char * setLevel = 0, * setName = 0;
static struct option longopts[] = {
{ "add", required_argument, 0, 'a' },
{ "create", no_argument, 0, 'c' },
{ "destroy", required_argument, 0, 'd' },
{ "erase", no_argument, 0, 'e' },
{ "header", no_argument, 0, 'h' },
{ "list", no_argument, 0, 'l' },
{ "modify", required_argument, 0, 'm' },
{ "remove", required_argument, 0, 'r' },
{ "spare", required_argument, 0, 's' },
{ "watch", no_argument, 0, 'w' },
{ "lvcreate", required_argument, 0, 'C' },
{ "lvdestroy", required_argument, 0, 'D' },
{ "lvlist", no_argument, 0, 'L' },
{ "lvmodify", required_argument, 0, 'M' },
{ "lvresize", required_argument, 0, 'R' },
{ "lvsnap", required_argument, 0, 'S' },
{ "auto-rebuild",required_argument, 0, 'A' },
{ "block-size", required_argument, 0, 'B' },
{ "extents", no_argument, 0, 'E' },
{ "hint", required_argument, 0, 'H' },
{ "level", required_argument, 0, 'V' },
{ "name", required_argument, 0, 'N' },
{ "quick-rebuild",required_argument, 0, 'Q' },
{ "size", required_argument, 0, 'Z' },
{ "timeout", required_argument, 0, 'T' },
{ "verbose", no_argument, 0, 'v' },
{ "help", no_argument, 0, '?' },
{ 0, 0, 0, 0 }
};
int ch;
while ((ch = getopt_long(argc, argv, "a:cd:ehlm:r:s:wC:D:LM:R:S:A:B:EH:V:N:Q:Z:T:v?", longopts, NULL)) != -1) {
switch(ch) {
case 'a':
add = true;
setName = strdup(optarg);
break;
case 'c':
create = true;
break;
case 'd':
destroy = true;
setName = strdup(optarg);
break;
case 'e':
erase = true;
break;
case 'h':
header = true;
break;
case 'l':
list = true;
break;
case 'm':
modify = true;
setName = strdup(optarg);
break;
case 'r':
remove = true;
setName = strdup(optarg);
break;
case 's':
spare = true;
setName = strdup(optarg);
break;
case 'w':
watch = true;
break;
case 'C':
lvcreate = true;
setName = strdup(optarg);
break;
case 'D':
lvdestroy = true;
setName = strdup(optarg);
break;
case 'L':
lvlist = true;
break;
case 'M':
lvmodify = true;
setName = strdup(optarg);
break;
case 'R':
lvresize = true;
setName = strdup(optarg);
break;
case 'S':
lvsnap = true;
setName = strdup(optarg);
break;
case 'A':
autoRebuild = ((optarg[0] == 'Y') || (optarg[0] == 'y')) ? AUTO_YES : AUTO_NO;
break;
case 'B':
sscanf(optarg, "%lli", &blockSize);
break;
case 'E':
extents = true;
break;
case 'H':
hint = strdup(optarg);
break;
case 'V':
setLevel = strdup(optarg);
break;
case 'N':
nickname = strdup(optarg);
break;
case 'Q':
quickRebuild = ((optarg[0] == 'Y') || (optarg[0] == 'y')) ? AUTO_YES : AUTO_NO;
break;
case 'Z':
sscanf(optarg, "%lli", &volSize);
break;
case 'T':
sscanf(optarg, "%lli", &timeout);
break;
case 'v':
verbose = true;
break;
case 0:
case '?':
default:
usage();
exit(0);
}
}
argc -= optind;
argv += optind;
if (!add && !create && !destroy && !erase && !header && !list && !modify && !remove && !spare && !watch &&
!lvcreate && !lvdestroy && !lvlist && !lvmodify && !lvresize && !lvsnap) {
usage();
exit(0);
}
if (list) {
listRAIDSets();
exit(0);
}
if (lvlist) {
listLogicalVolumes(NULL, argc, argv);
exit(0);
}
if (geteuid()) {
printf("ERROR: you must be super user for this operation.\n");
exit(1);
}
if (erase) {
erasePartition(argc, argv);
exit(0);
};
if (header) {
dumpHeader(argc, argv);
exit(0);
};
if (watch) {
CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
NULL, callBack,
CFSTR(kAppleRAIDNotificationSetDiscovered),
NULL, CFNotificationSuspensionBehaviorHold);
CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
NULL, callBack,
CFSTR(kAppleRAIDNotificationSetTerminated),
NULL, CFNotificationSuspensionBehaviorHold);
CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
NULL, callBack,
CFSTR(kAppleRAIDNotificationSetChanged),
NULL, CFNotificationSuspensionBehaviorHold);
CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
NULL, callBack,
CFSTR(kAppleLVMNotificationVolumeDiscovered),
NULL, CFNotificationSuspensionBehaviorHold);
CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
NULL, callBack,
CFSTR(kAppleLVMNotificationVolumeTerminated),
NULL, CFNotificationSuspensionBehaviorHold);
CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
NULL, callBack,
CFSTR(kAppleLVMNotificationVolumeChanged),
NULL, CFNotificationSuspensionBehaviorHold);
AppleRAIDEnableNotifications();
}
if (add) addMember(setName, CFSTR(kAppleRAIDMembersKey), argc, argv);
if (create) createSet(setLevel, nickname, argc, argv);
if (destroy) destroySet(setName, argc, argv);
if (modify) modifySet(setName, argc, argv);
if (remove) removeMember(setName, argc, argv);
if (spare) addMember(setName, CFSTR(kAppleRAIDSparesKey), argc, argv);
if (lvcreate) createLogicalVolume(setName, setLevel, argc, argv);
if (lvdestroy) destroyLogicalVolume(setName, argc, argv);
if (lvmodify) modifyLogicalVolume(setName, argc, argv);
if (lvresize) resizeLogicalVolume(setName, argc, argv);
if (lvsnap) snapshotLogicalVolume(setName, setLevel, argc, argv);
if (watch) {
printf("watching...\n");
sig_t oldHandler = signal(SIGINT, signalHandler);
if (oldHandler == SIG_ERR) {
printf("Could not establish new signal handler");
exit(1);
}
printf("Starting run loop.\n");
CFRunLoopRun();
printf("Unexpectedly back from CFRunLoopRun()!\n");
}
return 0;
}
static void
signalHandler(int sigraised)
{
printf("\nInterrupted.\n");
AppleRAIDDisableNotifications();
_exit(0);
}