/* * Copyright (c) 2001-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #include #include #include #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] \n"); printf("\n"); printf("artest --create --name --level disk1s3 disk2s3 disk3s3 ...\n"); printf("artest --destroy \n"); printf("\n"); printf("artest --add disk1s3 ...\n"); printf("artest --spare disk1s3 ...\n"); printf("artest --remove ...\n"); printf("artest --modify \n"); printf("\n"); printf("artest --lvcreate \n"); printf("artest --lvdestroy \n"); printf("\n"); printf("artest --lvmodify \n"); printf("artest --lvresize [--size ]\n"); printf("artest --lvsnap [--size ] --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(" = XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\n"); printf(" = \"the volume name\"\n"); printf(" = stripe, mirror, concat, lvg\n"); printf(" = Apple_HFS, RAIDNoMedia, RAIDNoFS\n"); printf(" = [--auto-rebuild=Yes,No] [--block-size=0x8000] [--hint=]\n"); printf(" = [--name=] [--timeout=30] [--quick-rebuild=Yes]\n"); printf(" = [--level=concat] [--hint=] [--name=] [--size 0xXXXXXXXXXXXXXXXX]\n"); printf("\n"); printf("global options:\n"); printf(" --verbose\n"); printf(" --watch (also a command)\n"); } // there must be something like this already? 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" // switches partition type static bool switchPartition(char * diskName, char * partitionType) { char wholeDevicePath[256]; sprintf(wholeDevicePath, "/dev/%s", diskName); unsigned int partitionNumber = 0; char * c = wholeDevicePath + 5 + 4; // skip over "/dev/disk" while (*c != 's' && *c++); // look for 's' if (*c == 's') { *c = 0; // clip off remainder sscanf(c+1, "%u", &partitionNumber); // get partition number } if (!partitionNumber) return true; // just assume it is a raid disk #define LIVERAID #ifdef LIVERAID char * optionString = " Writable Shared Writer "; #else char * optionString = " Writable "; #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; // find and extract the 'Schemes' array CFMutableArrayRef Schemes = (CFMutableArrayRef) CFDictionaryGetValue(media, CFSTR("Schemes")); if (!Schemes) goto Failure; // DMTool just grabs the first "default" scheme, so do the same CFMutableDictionaryRef Scheme = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(Schemes, 0); if (!Scheme) goto Failure; // Then find and extract the 'Sections' array of that scheme: CFMutableArrayRef Sections = (CFMutableArrayRef) CFDictionaryGetValue(Scheme, CFSTR("Sections")); if (!Sections) goto Failure; // Every scheme can have multiple sections to it, we need to find the 'MAP' section: CFMutableDictionaryRef Section = (CFMutableDictionaryRef) CFArrayDictionarySearch(Sections, CFSTR("ID"), CFSTR("MAP")); if (!Section) goto Failure; // Then find and extract the 'Partitions' array of that section: 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; // change the partition type (finally!) 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++; } // at this point the code should wait for notifications that members and the set are available } 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); // get list of available raid set descriptions 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++; } // at this point the code should wait for notifications that members and the set are available } 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--) { // printf("dumping the raid header on \"%s\".\n", *argv); 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); // get each set's properties 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); // get each member's properties 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]; // hack if (!nameCString) { usage(); exit(1); } CFStringRef lvUUID = CFStringCreateWithCString(kCFAllocatorDefault, nameCString, kCFStringEncodingUTF8); if (!lvUUID) exit(1); // try for one logical volume CFMutableDictionaryRef props = AppleLVMGetVolumeProperties(lvUUID); if (props) { dumpLogicalVolumeProperties(props); CFRelease(props); return; } // go for all volumes in the group 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; // create the snap CFMutableDictionaryRef snapProps = AppleLVMSnapShotVolume(lvProps, lvType, volSize); if (!snapProps) { printf("there was a problem initializing the snapshot for %s.\n", nameCString); exit(1); } // write to disk 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; /* options descriptor */ 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, // const void *observer callBack, CFSTR(kAppleRAIDNotificationSetDiscovered), NULL, // const void *object CFNotificationSuspensionBehaviorHold); CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), NULL, // const void *observer callBack, CFSTR(kAppleRAIDNotificationSetTerminated), NULL, // const void *object CFNotificationSuspensionBehaviorHold); CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), NULL, // const void *observer callBack, CFSTR(kAppleRAIDNotificationSetChanged), NULL, // const void *object CFNotificationSuspensionBehaviorHold); CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), NULL, // const void *observer callBack, CFSTR(kAppleLVMNotificationVolumeDiscovered), NULL, // const void *object CFNotificationSuspensionBehaviorHold); CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), NULL, // const void *observer callBack, CFSTR(kAppleLVMNotificationVolumeTerminated), NULL, // const void *object CFNotificationSuspensionBehaviorHold); CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), NULL, // const void *observer callBack, CFSTR(kAppleLVMNotificationVolumeChanged), NULL, // const void *object CFNotificationSuspensionBehaviorHold); // this will not fail if there is no raid controller, ie, if AppleRAID class is not instantiated in the kernel 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"); // Set up a signal handler so we can clean up when we're interrupted from the command line // Otherwise we stay in our run loop forever. sig_t oldHandler = signal(SIGINT, signalHandler); if (oldHandler == SIG_ERR) { printf("Could not establish new signal handler"); exit(1); } // Start the run loop. Now we'll receive notifications. // 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); }