1/*
2 * Copyright (c) 2001-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <unistd.h>
26#include <sys/param.h>
27
28#include <mach/mach.h>
29
30#include <CoreFoundation/CoreFoundation.h>
31
32#include <IOKit/IOKitLib.h>
33#include <IOKit/IOCFSerialize.h>
34#include <IOKit/IOCFUnserialize.h>
35#include <IOKit/IOMessage.h>
36
37#include <IOKit/IOBSD.h>
38#include <IOKit/storage/IOMedia.h>
39
40#include "AppleRAIDUserClient.h"
41#include "AppleRAIDUserLib.h"
42#include "AppleRAIDMember.h"
43#include "AppleLVMGroup.h"
44#include "AppleLVMVolume.h"
45#include "AppleRAIDConcatSet.h"
46#include "AppleRAIDMirrorSet.h"
47#include "AppleRAIDStripeSet.h"
48
49#ifdef DEBUG
50
51#define IOLog1(...) { printf(__VA_ARGS__); fflush(stdout); }
52//#define IOLog2(args...) { printf(args...); fflush(stdout); }
53
54#endif DEBUG
55
56#ifndef IOLog1
57#define IOLog1(args...)
58#endif
59#ifndef IOLog2
60#define IOLog2(args...)
61#endif
62
63// ***************************************************************************************************
64//
65// open/close raid controller connection
66//
67// ***************************************************************************************************
68
69static io_connect_t gRAIDControllerPort = 0;
70
71static io_connect_t
72AppleRAIDOpenConnection()
73{
74    kern_return_t	kr;
75    CFDictionaryRef	classToMatch;
76    io_service_t	serviceObject;
77
78    if (gRAIDControllerPort) return gRAIDControllerPort;
79
80    classToMatch = IOServiceMatching(kAppleRAIDUserClassName);
81    if (classToMatch == NULL)
82    {
83        IOLog1("IOServiceMatching returned a NULL dictionary.\n");
84	return kIOReturnNoResources;
85    }
86
87    serviceObject = IOServiceGetMatchingService(kIOMasterPortDefault, classToMatch);
88    if (!serviceObject)
89    {
90        IOLog2("Couldn't find any matches.\n");
91	return kIOReturnNoResources;
92    }
93
94    // This call will cause the user client to be instantiated.
95    kr = IOServiceOpen(serviceObject, mach_task_self(), 0, &gRAIDControllerPort);
96
97    // Release the io_service_t now that we're done with it.
98    IOObjectRelease(serviceObject);
99
100    if (kr != KERN_SUCCESS)
101    {
102        IOLog1("IOServiceOpen returned %d\n", kr);
103	return kr;
104    }
105
106    kr = IOConnectCallStructMethod(gRAIDControllerPort, kAppleRAIDClientOpen, 0, 0, 0, 0);
107    UInt32 count = 0;
108    // retry for 1 minute
109    while (kr == kIOReturnExclusiveAccess && count < 60)
110    {
111#ifdef DEBUG
112	if ((count % 15) == 0) IOLog1("AppleRAID: controller object is busy, retrying...\n");
113#endif
114	(void)sleep(1);
115	kr = IOConnectCallStructMethod(gRAIDControllerPort, kAppleRAIDClientOpen, 0, 0, 0, 0);
116	count++;
117    }
118    if (kr != KERN_SUCCESS)
119    {
120	printf("AppleRAID: failed trying to get controller object, rc = 0x%x.\n", kr);
121
122        // This closes the connection to our user client and destroys the connect handle.
123        IOServiceClose(gRAIDControllerPort);
124	gRAIDControllerPort = 0;
125    }
126
127    return gRAIDControllerPort;
128}
129
130static kern_return_t
131AppleRAIDCloseConnection()
132{
133    kern_return_t 	kr;
134
135    if (!gRAIDControllerPort) return kIOReturnSuccess;
136
137    kr = IOConnectCallStructMethod(gRAIDControllerPort, kAppleRAIDClientClose, 0, 0, 0, 0);
138    if (kr != KERN_SUCCESS)
139    {
140        IOLog1("AppleRAIDClientClose returned %d\n", kr);
141	return kr;
142    }
143
144    // This closes the connection to our user client and destroys the connect handle.
145    kr = IOServiceClose(gRAIDControllerPort);
146    if (kr != KERN_SUCCESS)
147    {
148        IOLog1("IOServiceClose returned %d\n", kr);
149	return kr;
150    }
151
152    gRAIDControllerPort = 0;
153
154    return kr;
155}
156
157
158// ***************************************************************************************************
159//
160// notifications
161//
162// ***************************************************************************************************
163
164typedef struct changeInfo {
165    io_object_t			service;
166    mach_port_t     		notifier;
167    CFStringRef			uuidString;
168} changeInfo_t;
169
170static IONotificationPortRef	gNotifyPort;
171static io_iterator_t		gRAIDSetIter;
172static io_iterator_t		gLogicalVolumeIter;
173
174static void
175raidSetChanged(void *refcon, io_service_t service, natural_t messageType, void *messageArgument)
176{
177    changeInfo_t * changeInfo = (changeInfo_t *)refcon;
178
179    if (messageType == kIOMessageServiceIsTerminated) {
180
181	// broadcast "raid set died" notification
182	CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(),
183					     CFSTR(kAppleRAIDNotificationSetTerminated),
184					     changeInfo->uuidString,
185					     NULL,           // CFDictionaryRef userInfo
186					     false);
187
188	IOObjectRelease(changeInfo->service);
189	IOObjectRelease(changeInfo->notifier);
190	CFRelease(changeInfo->uuidString);
191	free(changeInfo);
192
193	return;
194    }
195
196    IOLog2("raidSetChanged: messageType %08x, arg %08lx\n", messageType, (UInt32) messageArgument);
197
198    // we only care about messages from the raid driver, toss all others.
199    if (messageType != kAppleRAIDMessageSetChanged) return;
200
201    // broadcast "raid set changed" notification
202    CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(),
203					 CFSTR(kAppleRAIDNotificationSetChanged),
204					 changeInfo->uuidString,
205					 NULL,           // CFDictionaryRef userInfo
206					 false);
207}
208
209void static
210raidSetDetected(void *refCon, io_iterator_t iterator)
211{
212    kern_return_t		kr;
213    io_service_t		newSet;
214    changeInfo_t *		changeInfo;
215    CFMutableDictionaryRef  	registryEntry;
216    CFStringRef			uuidString;
217
218    while (newSet = IOIteratorNext(iterator)) {
219
220	// fetch a copy of the in kernel registry object
221	kr = IORegistryEntryCreateCFProperties(newSet, &registryEntry, kCFAllocatorDefault, 0);
222	if (kr != KERN_SUCCESS) return;
223
224	// get the set's UUID name, for stacked sets the member uuid is the correct UUID
225	// to use for this notification, it also works for regular raid sets.
226	uuidString = CFDictionaryGetValue(registryEntry, CFSTR(kAppleRAIDMemberUUIDKey));
227	if (uuidString) uuidString = CFStringCreateCopy(NULL, uuidString);
228	CFRelease(registryEntry);
229	if (!uuidString) return;
230
231	changeInfo = calloc(1, sizeof(changeInfo_t));
232	changeInfo->service = newSet;
233	changeInfo->uuidString = uuidString;
234
235	// set up notifications for any changes to this set
236	kr = IOServiceAddInterestNotification(gNotifyPort, newSet, kIOGeneralInterest,
237					      &raidSetChanged, (void *)changeInfo,
238					      &changeInfo->notifier);
239	if (kr != KERN_SUCCESS) {
240	    free(changeInfo);
241	    return;
242	}
243
244	// broadcast "new raid set" notification
245	CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(),
246					     CFSTR(kAppleRAIDNotificationSetDiscovered),
247					     uuidString,
248					     NULL,	// CFDictionaryRef userInfo
249					     false);
250    }
251}
252
253
254static void
255logicalVolumeChanged(void *refcon, io_service_t service, natural_t messageType, void *messageArgument)
256{
257    changeInfo_t * changeInfo = (changeInfo_t *)refcon;
258
259    if (messageType == kIOMessageServiceIsTerminated) {
260
261	// broadcast "logical volume died" notification
262	CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(),
263					     CFSTR(kAppleLVMNotificationVolumeTerminated),
264					     changeInfo->uuidString,
265					     NULL,           // CFDictionaryRef userInfo
266					     false);
267
268	IOObjectRelease(changeInfo->service);
269	IOObjectRelease(changeInfo->notifier);
270	CFRelease(changeInfo->uuidString);
271	free(changeInfo);
272
273	return;
274    }
275
276    IOLog2("logicalVolumeChanged: messageType %08x, arg %08lx\n", messageType, (UInt32) messageArgument);
277
278    // we only care about messages from the raid driver, toss all others.
279    if (messageType != kAppleLVMMessageVolumeChanged) return;
280
281    // broadcast "logical volume changed" notification
282    CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(),
283					 CFSTR(kAppleLVMNotificationVolumeChanged),
284					 changeInfo->uuidString,
285					 NULL,           // CFDictionaryRef userInfo
286					 false);
287}
288
289void static
290logicalVolumeDetected(void *refCon, io_iterator_t iterator)
291{
292    kern_return_t		kr;
293    io_service_t		newVolume;
294    changeInfo_t *		changeInfo;
295    CFMutableDictionaryRef  	registryEntry;
296    CFStringRef			uuidString;
297
298    while (newVolume = IOIteratorNext(iterator)) {
299
300	// fetch a copy of the in kernel registry object
301	kr = IORegistryEntryCreateCFProperties(newVolume, &registryEntry, kCFAllocatorDefault, 0);
302	if (kr != KERN_SUCCESS) return;
303
304	// get the volume's UUID name
305	uuidString = CFDictionaryGetValue(registryEntry, CFSTR("UUID"));
306	if (uuidString) uuidString = CFStringCreateCopy(NULL, uuidString);
307	CFRelease(registryEntry);
308	if (!uuidString) return;
309
310	changeInfo = calloc(1, sizeof(changeInfo_t));
311	changeInfo->service = newVolume;
312	changeInfo->uuidString = uuidString;
313
314	// set up notifications for any changes to this volume
315	kr = IOServiceAddInterestNotification(gNotifyPort, newVolume, kIOGeneralInterest,
316					      &logicalVolumeChanged, (void *)changeInfo,
317					      &changeInfo->notifier);
318	if (kr != KERN_SUCCESS) {
319	    free(changeInfo);
320	    return;
321	}
322
323	// broadcast "new raid volume" notification
324	CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(),
325					     CFSTR(kAppleLVMNotificationVolumeDiscovered),
326					     uuidString,
327					     NULL,	// CFDictionaryRef userInfo
328					     false);
329    }
330}
331
332
333kern_return_t
334AppleRAIDEnableNotifications()
335{
336    kern_return_t 	kr;
337    CFDictionaryRef	classToMatch;
338    CFRunLoopSourceRef	runLoopSource;
339
340    IOLog1("AppleRAIDEnableNotifications entered\n");
341
342    gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault);
343    runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort);
344
345    CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
346
347    //
348    // set up raid set notifications
349    //
350
351    classToMatch = IOServiceMatching(kAppleRAIDSetClassName);
352    if (classToMatch == NULL)
353    {
354        IOLog1("IOServiceMatching returned a NULL dictionary.\n");
355	return kIOReturnNoResources;
356    }
357
358    kr = IOServiceAddMatchingNotification(  gNotifyPort,
359                                            kIOFirstMatchNotification,
360                                            classToMatch,
361                                            raidSetDetected,
362                                            NULL,
363                                            &gRAIDSetIter );
364    if (kr != KERN_SUCCESS)
365    {
366        IOLog1("IOServiceAddMatchingNotification returned %d\n", kr);
367	return kr;
368    }
369
370    raidSetDetected(NULL, gRAIDSetIter);	// Iterate once to get already-present
371						// devices and arm the notification
372    //
373    // set up logical volume notifications
374    //
375
376    classToMatch = IOServiceMatching(kAppleLogicalVolumeClassName);
377    if (classToMatch == NULL)
378    {
379        IOLog1("IOServiceMatching returned a NULL dictionary.\n");
380	return kIOReturnNoResources;
381    }
382
383    kr = IOServiceAddMatchingNotification(  gNotifyPort,
384                                            kIOFirstMatchNotification,
385                                            classToMatch,
386                                            logicalVolumeDetected,
387                                            NULL,
388                                            &gLogicalVolumeIter );
389    if (kr != KERN_SUCCESS)
390    {
391        IOLog1("IOServiceAddMatchingNotification returned %d\n", kr);
392	return kr;
393    }
394
395    logicalVolumeDetected(NULL, gLogicalVolumeIter);	// Iterate once to get already-present
396							// devices and arm the notification
397    return kr;
398}
399
400kern_return_t
401AppleRAIDDisableNotifications(void)
402{
403
404    IONotificationPortDestroy(gNotifyPort);
405
406    if (gRAIDSetIter) {
407        IOObjectRelease(gRAIDSetIter);
408        gRAIDSetIter = 0;
409    }
410    if (gLogicalVolumeIter) {
411        IOObjectRelease(gLogicalVolumeIter);
412        gLogicalVolumeIter = 0;
413    }
414
415    return KERN_SUCCESS;
416}
417
418
419// ***************************************************************************************************
420//
421// list of set, getSet, getMember
422//
423// ***************************************************************************************************
424
425typedef struct memberInfo {
426    CFStringRef diskNameCF;
427    io_name_t	diskName;
428    io_name_t	wholeDiskName;
429    unsigned int partitionNumber;
430    char	devicePath[256];
431    io_name_t	regName;
432
433    // from media
434    UInt64	size;
435    UInt64	blockSize;
436    bool	isWhole;
437    bool	isRAID;
438    CFStringRef uuidString;
439    UInt64	headerOffset;
440
441    // from header
442    UInt64	chunkCount;
443    UInt64	chunkSize;
444    UInt64	primaryMetaDataSize;
445    UInt64	secondaryMetaDataSize;
446    UInt64	startOffset;		// jbod & lvg
447
448    AppleRAIDPrimaryOnDisk * primaryData;
449    void *	secondaryData;
450} memberInfo_t;
451
452static void
453freeMemberInfo(memberInfo_t * m)
454{
455    if (m->diskNameCF) CFRelease(m->diskNameCF);
456    if (m->uuidString) CFRelease(m->uuidString);
457    if (m->primaryData) free(m->primaryData);
458    if (m->secondaryData) free(m->secondaryData);
459    free(m);
460}
461
462static memberInfo_t *
463getMemberInfo(CFStringRef partitionName)
464{
465    // sigh...
466    CFIndex diskNameSize = CFStringGetLength(partitionName);
467    diskNameSize = CFStringGetMaximumSizeForEncoding(diskNameSize, kCFStringEncodingUTF8) + 1;
468    char *diskName = malloc(diskNameSize);
469    if (!CFStringGetCString(partitionName, diskName, diskNameSize, kCFStringEncodingUTF8)) return NULL;
470
471    io_registry_entry_t obj = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, diskName));
472    if (!obj){
473        IOLog1("AppleRAIDLib - getMemberInfo: IOServiceGetMatchingService failed for %s\n", diskName);
474	return NULL;
475    }
476
477    memberInfo_t * mi = calloc(1, sizeof(memberInfo_t));
478
479    mi->diskNameCF = partitionName;
480    CFRetain(partitionName);
481
482    strlcpy(mi->diskName, diskName, sizeof(io_name_t));
483    snprintf(mi->devicePath, sizeof(mi->devicePath), "/dev/%s", diskName);
484
485    IORegistryEntryGetName(obj, mi->regName);
486
487    CFMutableDictionaryRef properties = NULL;
488    IOReturn result = IORegistryEntryCreateCFProperties(obj, &properties, kCFAllocatorDefault, kNilOptions);
489
490    if (!result && properties) {
491
492	CFNumberRef number;
493
494	number = (CFNumberRef)CFDictionaryGetValue(properties, CFSTR(kIOMediaSizeKey));
495	if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &mi->size);
496
497	mi->headerOffset = ARHEADER_OFFSET(mi->size);
498
499	number = (CFNumberRef)CFDictionaryGetValue(properties, CFSTR(kIOMediaPreferredBlockSizeKey));
500	if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &mi->blockSize);
501
502	mi->isWhole = (CFBooleanRef)CFDictionaryGetValue(properties, CFSTR(kIOMediaWholeKey)) == kCFBooleanTrue;
503
504	mi->isRAID = (CFBooleanRef)CFDictionaryGetValue(properties, CFSTR(kAppleRAIDIsRAIDKey)) == kCFBooleanTrue;
505
506	mi->uuidString = (CFStringRef)CFDictionaryGetValue(properties, CFSTR(kIOMediaUUIDKey));
507	if (mi->uuidString) CFRetain(mi->uuidString);
508
509	strcpy(mi->wholeDiskName, mi->diskName);
510
511	if (!mi->isWhole) {
512	    char * c = mi->wholeDiskName + 4;				// skip over disk
513	    while (*c != 's' && *c++);					// look for 's'
514	    if (*c == 's') {
515		*c = 0;							// clip off remainder
516		sscanf(c+1, "%u", &mi->partitionNumber);		// get partition number
517	    }
518	}
519
520    } else {
521	freeMemberInfo(mi);
522	return NULL;
523    }
524
525    return mi;
526}
527
528typedef struct setInfo {
529    CFMutableDictionaryRef	setProps;
530    CFIndex			memberCount;
531    CFMutableArrayRef		members;
532    CFMutableDictionaryRef *	memberProps;
533    memberInfo_t **		memberInfo;
534//    CFIndex			spareCount;
535//    CFMutableArrayRef		spares;
536//    CFMutableDictionaryRef	spareProps;
537//    memberInfo_t **		spareInfo;
538} setInfo_t;
539
540static void
541freeSetInfo(setInfo_t *setInfo)
542{
543    CFIndex i;
544    if (setInfo->memberProps) {
545	for (i=0; i < setInfo->memberCount; i++) {
546	    if (setInfo->memberProps[i]) CFRelease(setInfo->memberProps[i]);
547	}
548	free(setInfo->memberProps);
549    }
550    if (setInfo->memberInfo) {
551	for (i=0; i < setInfo->memberCount; i++) {
552	    if (setInfo->memberInfo[i]) freeMemberInfo(setInfo->memberInfo[i]);
553	}
554	free(setInfo->memberInfo);
555    }
556
557    // XXX same for spares
558
559    if (setInfo->setProps) CFRelease(setInfo->setProps);
560    free(setInfo);
561}
562
563static setInfo_t *
564getSetInfo(AppleRAIDSetRef setRef)
565{
566    setInfo_t * setInfo = calloc(1, sizeof(setInfo_t));
567    if (!setInfo) return NULL;
568
569    setInfo->setProps = AppleRAIDGetSetProperties(setRef);
570    if (!setInfo->setProps) goto error;
571
572    // find the members/spares in the this set
573    setInfo->members = (CFMutableArrayRef)CFDictionaryGetValue(setInfo->setProps, CFSTR(kAppleRAIDMembersKey));
574    setInfo->memberCount = setInfo->members ? CFArrayGetCount(setInfo->members) : 0;
575//    setInfo->spares = (CFMutableArrayRef)CFDictionaryGetValue(setInfo->setProps, CFSTR(kAppleRAIDSparesKey));
576//    setInfo->spareCount = setInfo->spares ? CFArrayGetCount(setInfo->spares) : 0;
577
578    if (setInfo->memberCount) {
579	setInfo->memberInfo = calloc(setInfo->memberCount, sizeof(memberInfo_t *));
580	setInfo->memberProps = calloc(setInfo->memberCount, sizeof(CFMutableDictionaryRef));
581    }
582//    if (setInfo->spareCount) {
583//	setInfo->spareInfo = calloc(setInfo->spareCount, sizeof(memberInfo_t *));
584//	setInfo->spareProps = calloc(setInfo->spareCount, sizeof(CFMutableDictionaryRef));
585//    }
586
587    CFIndex i;
588    for (i=0; i < setInfo->memberCount; i++) {
589	CFStringRef member = (CFStringRef)CFArrayGetValueAtIndex(setInfo->members, i);
590	if (member) {
591	    setInfo->memberProps[i] = AppleRAIDGetMemberProperties(member);
592	    if (setInfo->memberProps[i]) {
593		CFStringRef partitionName = (CFStringRef)CFDictionaryGetValue(setInfo->memberProps[i], CFSTR(kIOBSDNameKey));
594		if (partitionName) {
595		    setInfo->memberInfo[i] = getMemberInfo(partitionName);
596		}
597
598		CFMutableDictionaryRef props = setInfo->memberProps[i];
599		memberInfo_t * info = setInfo->memberInfo[i];
600
601		CFNumberRef number;
602		number = (CFNumberRef)CFDictionaryGetValue(setInfo->setProps, CFSTR(kAppleRAIDChunkSizeKey));   // per set
603		if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &info->chunkSize);
604
605		number = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kAppleRAIDChunkCountKey));		// per member
606		if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &info->chunkCount);
607
608		number = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kAppleRAIDPrimaryMetaDataUsedKey));	// per member
609		if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &info->primaryMetaDataSize);
610
611		number = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kAppleRAIDSecondaryMetaDataSizeKey));	// per member
612		if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &info->secondaryMetaDataSize);
613
614		number = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kAppleRAIDMemberStartKey));		// per member
615		if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &info->startOffset);
616	    }
617	}
618    }
619
620    // XXX same for spares
621
622    return setInfo;
623
624error:
625    freeSetInfo(setInfo);
626    return NULL;
627}
628
629
630#define kMaxIOConnectTransferSize  4096
631
632CFMutableArrayRef
633AppleRAIDGetListOfSets(UInt32 filter)
634{
635    kern_return_t 	kr;
636    size_t		listSize = kMaxIOConnectTransferSize;
637    CFMutableArrayRef 	theList = NULL;
638
639    char * listString = (char *)malloc((int)listSize);
640
641    io_connect_t raidControllerPort = AppleRAIDOpenConnection();
642    if (!raidControllerPort) return NULL;
643
644    kr = IOConnectCallStructMethod(raidControllerPort,		// an io_connect_t returned from IOServiceOpen().
645				   kAppleRAIDGetListOfSets,	// an index to the function in the Kernel.
646				   &filter,			// input
647				   sizeof(filter),		// input size
648				   listString,			// output
649				   &listSize);			// output size (in/out)
650    if (kr == KERN_SUCCESS) {
651        IOLog2("AppleRAIDGetListOfSets was successful.\n");
652        IOLog2("size = %d, theList = %s\n", (int)listSize, (char *)listString);
653
654	theList = (CFMutableArrayRef)IOCFUnserialize(listString, kCFAllocatorDefault, 0, NULL);
655    }
656
657    free(listString);
658
659    AppleRAIDCloseConnection();
660
661    return theList;
662}
663
664CFMutableDictionaryRef
665AppleRAIDGetSetProperties(AppleRAIDSetRef setName)
666{
667    kern_return_t 	kr;
668    size_t		propSize = kMaxIOConnectTransferSize;
669    CFMutableDictionaryRef props = NULL;
670    size_t		bufferSize = kAppleRAIDMaxUUIDStringSize;
671    char		buffer[bufferSize];
672
673    if (!CFStringGetCString(setName, buffer, bufferSize, kCFStringEncodingUTF8)) {
674	IOLog1("AppleRAIDGetSetProperties() CFStringGetCString failed?\n");
675	return NULL;
676    }
677
678    io_connect_t raidControllerPort = AppleRAIDOpenConnection();
679    if (!raidControllerPort) return NULL;
680
681    char * propString = (char *)malloc(propSize);
682
683    kr = IOConnectCallStructMethod(raidControllerPort,		// an io_connect_t returned from IOServiceOpen().
684				   kAppleRAIDGetSetProperties,	// an index to the function in the Kernel.
685				   buffer,			// input
686				   bufferSize,			// input size
687				   propString,			// output
688				   &propSize);			// output size (in/out)
689
690    if (kr == KERN_SUCCESS) {
691        IOLog2("AppleRAIDGetSetProperties was successful.\n");
692        IOLog2("size = %d, prop = %s\n", (int)propSize, (char *)propString);
693
694	props = (CFMutableDictionaryRef)IOCFUnserialize(propString, kCFAllocatorDefault, 0, NULL);
695    }
696
697    free(propString);
698
699    AppleRAIDCloseConnection();
700
701    return props;
702}
703
704CFMutableDictionaryRef
705AppleRAIDGetMemberProperties(AppleRAIDMemberRef memberName)
706{
707    kern_return_t 	kr;
708    size_t		propSize = kMaxIOConnectTransferSize;
709    CFMutableDictionaryRef props = NULL;
710    size_t		bufferSize = kAppleRAIDMaxUUIDStringSize;
711    char		buffer[bufferSize];
712
713    if (!CFStringGetCString(memberName, buffer, bufferSize, kCFStringEncodingUTF8)) {
714	IOLog1("AppleRAIDGetMemberProperties() CFStringGetCString failed?\n");
715	return NULL;
716    }
717
718    io_connect_t raidControllerPort = AppleRAIDOpenConnection();
719    if (!raidControllerPort) return NULL;
720
721    char * propString = (char *)malloc(propSize);
722
723    kr = IOConnectCallStructMethod(raidControllerPort,		// an io_connect_t returned from IOServiceOpen().
724				   kAppleRAIDGetMemberProperties,	// an index to the function in the Kernel.
725				   buffer,			// input
726				   bufferSize,			// input size
727				   propString,			// output
728				   &propSize);			// output size (in/out)
729
730    if (kr == KERN_SUCCESS) {
731        IOLog2("AppleRAIDGetMemberProperties was successful.\n");
732        IOLog2("size = %d, prop = %s\n", (int)propSize, (char *)propString);
733
734	props = (CFMutableDictionaryRef)IOCFUnserialize(propString, kCFAllocatorDefault, 0, NULL);
735    }
736
737    free(propString);
738
739    AppleRAIDCloseConnection();
740
741    return props;
742}
743
744// ***************************************************************************************************
745//
746// set creation
747//
748// ***************************************************************************************************
749
750// XXX this should really be read out of a set files, one for each raid type
751static const char *raidDescriptionBuffer =
752" <array> \n"
753    "<dict> \n"
754	"<key>" kAppleRAIDLevelNameKey "</key>"		"<string>" kAppleRAIDLevelNameStripe "</string> \n"
755	"<key>" kAppleRAIDMemberTypeKey "</key>"	"<array> \n"
756								"<string>" kAppleRAIDMembersKey "</string> \n"
757							"</array> \n"
758	"<key>" kAppleRAIDSetAutoRebuildKey "</key>"	"<false/> \n"
759	"<key>" kAppleRAIDSetQuickRebuildKey "</key>"	"<false/> \n"
760	"<key>" kAppleRAIDSetTimeoutKey "</key>"	"<integer size=\"32\">0</integer> \n"
761	"<key>" kAppleRAIDChunkSizeKey "</key>"		"<integer size=\"64\">0x8000</integer> \n"
762
763	"<key>" kAppleRAIDCanAddMembersKey "</key>"	"<false/> \n"
764	"<key>" kAppleRAIDCanAddSparesKey "</key>"	"<false/> \n"
765	"<key>" kAppleRAIDSizesCanVaryKey "</key>"	"<false/> \n"
766	"<key>" kAppleRAIDRemovalAllowedKey "</key>"	"<string>" kAppleRAIDRemovalNone "</string> \n"
767
768	"<key>" kAppleRAIDCanBeConvertedToKey "</key>"	"<false/> \n"
769    "</dict> \n"
770    "<dict> \n"
771	"<key>" kAppleRAIDLevelNameKey "</key>"		"<string>" kAppleRAIDLevelNameMirror "</string> \n"
772	"<key>" kAppleRAIDMemberTypeKey "</key>"	"<array> \n"
773								"<string>" kAppleRAIDMembersKey "</string> \n"
774								"<string>" kAppleRAIDSparesKey "</string> \n"
775							"</array> \n"
776	"<key>" kAppleRAIDSetAutoRebuildKey "</key>"	"<true/> \n"
777	"<key>" kAppleRAIDSetQuickRebuildKey "</key>"	"<true/> \n"
778	"<key>" kAppleRAIDSetTimeoutKey "</key>"	"<integer size=\"32\">30</integer> \n"
779	"<key>" kAppleRAIDChunkSizeKey "</key>"		"<integer size=\"64\">0x8000</integer> \n"
780
781	"<key>" kAppleRAIDCanAddMembersKey "</key>"	"<true/> \n"
782	"<key>" kAppleRAIDCanAddSparesKey "</key>"	"<true/> \n"
783	"<key>" kAppleRAIDSizesCanVaryKey "</key>"	"<false/> \n"
784	"<key>" kAppleRAIDRemovalAllowedKey "</key>"	"<string>" kAppleRAIDRemovalAnyMember "</string> \n"
785
786	"<key>" kAppleRAIDCanBeConvertedToKey "</key>"	"<true/> \n"
787    "</dict> \n"
788    "<dict> \n"
789	"<key>" kAppleRAIDLevelNameKey "</key>"		"<string>" kAppleRAIDLevelNameConcat "</string> \n"
790	"<key>" kAppleRAIDMemberTypeKey "</key>"	"<array> \n"
791								"<string>" kAppleRAIDMembersKey "</string> \n"
792							"</array> \n"
793	"<key>" kAppleRAIDSetAutoRebuildKey "</key>"	"<false/> \n"
794	"<key>" kAppleRAIDSetQuickRebuildKey "</key>"	"<false/> \n"
795	"<key>" kAppleRAIDSetTimeoutKey "</key>"	"<integer size=\"32\">0</integer> \n"
796	"<key>" kAppleRAIDChunkSizeKey "</key>"		"<integer size=\"64\">0x8000</integer> \n"
797
798	"<key>" kAppleRAIDCanAddMembersKey "</key>"	"<true/> \n"
799	"<key>" kAppleRAIDCanAddSparesKey "</key>"	"<false/> \n"
800	"<key>" kAppleRAIDSizesCanVaryKey "</key>"	"<true/> \n"
801	"<key>" kAppleRAIDRemovalAllowedKey "</key>"	"<string>" kAppleRAIDRemovalLastMember "</string> \n"
802
803	"<key>" kAppleRAIDCanBeConvertedToKey "</key>"	"<true/> \n"
804    "</dict> \n"
805    "<dict> \n"
806	"<key>" kAppleRAIDLevelNameKey "</key>"		"<string>" kAppleRAIDLevelNameLVG "</string> \n"
807	"<key>" kAppleRAIDMemberTypeKey "</key>"	"<array> \n"
808								"<string>" kAppleRAIDMembersKey "</string> \n"
809							"</array> \n"
810	"<key>" kAppleRAIDSetAutoRebuildKey "</key>"	"<false/> \n"
811	"<key>" kAppleRAIDSetTimeoutKey "</key>"	"<integer size=\"32\">0</integer> \n"
812	"<key>" kAppleRAIDChunkSizeKey "</key>"		"<integer size=\"64\">0x8000</integer> \n"
813
814	"<key>" kAppleRAIDCanAddMembersKey "</key>"	"<true/> \n"
815	"<key>" kAppleRAIDCanAddSparesKey "</key>"	"<false/> \n"
816	"<key>" kAppleRAIDSizesCanVaryKey "</key>"	"<true/> \n"
817	"<key>" kAppleRAIDRemovalAllowedKey "</key>"	"<string>" kAppleRAIDRemovalNone "</string> \n"
818
819	"<key>" kAppleRAIDCanBeConvertedToKey "</key>"	"<true/> \n"
820    "</dict> \n"
821" </array> \n";
822
823
824CFMutableArrayRef AppleRAIDGetSetDescriptions(void)
825{
826    CFStringRef errorString;
827
828    CFMutableArrayRef setDescriptions = (CFMutableArrayRef)IOCFUnserialize(raidDescriptionBuffer, kCFAllocatorDefault, 0, &errorString);
829    if (!setDescriptions) {
830	CFIndex	bufferSize = CFStringGetLength(errorString);
831	bufferSize = CFStringGetMaximumSizeForEncoding(bufferSize, kCFStringEncodingUTF8) + 1;
832	char *buffer = malloc(bufferSize);
833	if (!buffer || !CFStringGetCString(errorString, buffer, bufferSize, kCFStringEncodingUTF8)) {
834	    return NULL;
835	}
836
837	IOLog1("AppleRAIDGetSetDescriptions - failed while parsing raid definition file, error: %s\n", buffer);
838	CFRelease(errorString);
839	return NULL;
840    }
841
842    return setDescriptions;
843}
844
845
846// XXX this should really be read out of a file based on raidType
847// XXX timeouts don't apply to stripes, ...
848static const char *defaultCreateSetBuffer =
849" <dict> \n"
850    "<key>" kAppleRAIDHeaderVersionKey "</key>"		"<integer size=\"32\">0x00020000</integer> \n"
851    "<key>" kAppleRAIDLevelNameKey "</key>"		"<string>internal error</string> \n"
852    "<key>" kAppleRAIDSetNameKey "</key>"		"<string>internal error</string> \n"
853    "<key>" kAppleRAIDSetUUIDKey "</key>"		"<string>internal error</string> \n"
854    "<key>" kAppleRAIDSequenceNumberKey "</key>"	"<integer size=\"32\">1</integer> \n"
855    "<key>" kAppleRAIDChunkSizeKey "</key>"		"<integer size=\"64\">0x00008000</integer> \n"
856    "<key>" kAppleRAIDChunkCountKey "</key>"		"<integer size=\"64\">0</integer> \n"		// per member
857
858    "<key>" kAppleRAIDMembersKey "</key>"		"<array/> \n"
859    "<key>" kAppleRAIDSparesKey "</key>"		"<array/> \n"
860
861    "<key>" kAppleRAIDSetAutoRebuildKey "</key>"	"<false/> \n"					// mirror, raid v only
862    "<key>" kAppleRAIDSetQuickRebuildKey "</key>"	"<false/> \n"					// mirror, raid v only
863    "<key>" kAppleRAIDSetTimeoutKey "</key>"		"<integer size=\"32\">30</integer> \n"		// mirror, raid v only
864
865    "<key>" kAppleRAIDCanAddMembersKey "</key>"		"<false/> \n"					// mirror, concat only
866    "<key>" kAppleRAIDCanAddSparesKey "</key>"		"<false/> \n"
867    "<key>" kAppleRAIDSizesCanVaryKey "</key>"		"<false/> \n"					// true for concat only
868    "<key>" kAppleRAIDRemovalAllowedKey "</key>"	"<string>internal error</string> \n"
869
870    "<key>" kAppleRAIDSetContentHintKey "</key>"	"<string/> \n"
871" </dict> \n";
872
873
874CFMutableDictionaryRef
875AppleRAIDCreateSet(CFStringRef raidType, CFStringRef setName)
876{
877    CFStringRef errorString;
878
879    CFMutableDictionaryRef setProps = (CFMutableDictionaryRef)IOCFUnserialize(defaultCreateSetBuffer, kCFAllocatorDefault, 0, &errorString);
880    if (!setProps) {
881	CFIndex	bufferSize = CFStringGetLength(errorString);
882	bufferSize = CFStringGetMaximumSizeForEncoding(bufferSize, kCFStringEncodingUTF8) + 1;
883	char *buffer = malloc(bufferSize);
884	if (!buffer || !CFStringGetCString(errorString, buffer, bufferSize, kCFStringEncodingUTF8)) {
885	    return NULL;
886	}
887
888	IOLog1("AppleRAIDCreateSet - failed while parsing create set template file, error: %s\n", buffer);
889	CFRelease(errorString);
890	return NULL;
891    }
892
893    CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
894    if (!uuid) return NULL;
895    CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuid);
896    CFRelease(uuid);
897    if (!uuidString) return NULL;
898
899    CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDSetUUIDKey), uuidString);  CFRelease(uuidString);
900    CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDLevelNameKey), raidType);
901    CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDSetNameKey), setName);
902
903    // XXX could just pull these from GetSetDescriptions
904    // AppleRAIDDefaultSetPropForKey(raidType, key);
905
906    // overrides
907    if (CFEqual(raidType, CFSTR("Stripe"))) {
908	CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDRemovalAllowedKey), CFSTR(kAppleRAIDRemovalNone));
909    }
910    if (CFEqual(raidType, CFSTR("Concat"))) {
911	CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDCanAddMembersKey), kCFBooleanTrue);
912	CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDSizesCanVaryKey), kCFBooleanTrue);
913	CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDRemovalAllowedKey), CFSTR(kAppleRAIDRemovalLastMember));
914    }
915    if (CFEqual(raidType, CFSTR("Mirror"))) {
916	CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDCanAddMembersKey), kCFBooleanTrue);
917	CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDCanAddSparesKey), kCFBooleanTrue);
918	CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDRemovalAllowedKey), CFSTR(kAppleRAIDRemovalAnyMember));
919    }
920    if (CFEqual(raidType, CFSTR("LVG"))) {
921	CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDCanAddMembersKey), kCFBooleanTrue);
922	CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDSizesCanVaryKey), kCFBooleanTrue);
923	CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDRemovalAllowedKey), CFSTR(kAppleRAIDRemovalAnyMember));
924	CFDictionaryReplaceValue(setProps, CFSTR(kAppleRAIDSetContentHintKey), CFSTR(kAppleRAIDNoMediaExport));
925    }
926
927    return setProps;
928}
929
930bool
931AppleRAIDModifySet(CFMutableDictionaryRef setProps, CFStringRef key, void * value)
932{
933    CFStringRef errorString;
934
935//  AppleRAIDDefaultSetPropForKey(raidType, key);
936
937    CFMutableDictionaryRef defaultSetProps = (CFMutableDictionaryRef)IOCFUnserialize(defaultCreateSetBuffer, kCFAllocatorDefault, 0, &errorString);
938    if (!defaultSetProps) {
939	CFIndex	bufferSize = CFStringGetLength(errorString);
940	bufferSize = CFStringGetMaximumSizeForEncoding(bufferSize, kCFStringEncodingUTF8) + 1;
941	char *buffer = malloc(bufferSize);
942	if (!buffer || !CFStringGetCString(errorString, buffer, bufferSize, kCFStringEncodingUTF8)) {
943	    goto error;
944	}
945
946	IOLog1("AppleRAIDModifySet - failed while parsing create set template file, error: %s\n", buffer);
947	CFRelease(errorString);
948	goto error;
949    }
950
951    const void * defaultValue = CFDictionaryGetValue(defaultSetProps, key);
952    if (!defaultValue) goto error;
953
954    if (CFGetTypeID(defaultValue) != CFGetTypeID(value)) goto error;
955
956    // XXX if live, changing the chunksize means we have to change chunk count
957
958    CFDictionarySetValue(setProps, key, value);
959
960    CFRelease(defaultSetProps);
961
962    return true;
963
964error:
965    if (defaultSetProps) CFRelease(defaultSetProps);
966    return false;
967}
968
969// ***************************************************************************************************
970//
971// member creation
972//
973// ***************************************************************************************************
974
975AppleRAIDMemberRef
976AppleRAIDAddMember(CFMutableDictionaryRef setProps, CFStringRef partitionName, CFStringRef memberType)
977{
978    memberInfo_t * memberInfo = getMemberInfo(partitionName);
979    if (!memberInfo) return NULL;
980
981    // whole raw disks are not supported
982    if ((memberInfo->isWhole) && (!memberInfo->isRAID)) return NULL;
983
984    // make sure we support this operation
985    UInt32 version;
986    CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDHeaderVersionKey));
987    if (!number || !CFNumberGetValue(number, kCFNumberSInt32Type, &version) || version < 0x00020000) {
988	printf("AppleRAID: This operation is not supported on earlier RAID set revisions.\n");
989	return NULL;
990    }
991
992    // get/build UUID string
993    CFStringRef uuidString = 0;
994    if (memberInfo->isRAID) {
995	uuidString = memberInfo->uuidString;
996	if (uuidString) CFRetain(uuidString);
997    } else {
998	CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
999	if (!uuid) return NULL;
1000	uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuid);
1001	CFRelease(uuid);
1002    }
1003    freeMemberInfo(memberInfo);
1004    if (!uuidString) return NULL;
1005
1006    CFMutableArrayRef uuidArray = (CFMutableArrayRef)CFDictionaryGetValue(setProps, memberType);
1007    if (!uuidArray) return NULL;
1008    // make sure that uuidArray is resizable
1009    uuidArray = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, uuidArray);
1010    if (!uuidArray) return NULL;
1011    CFDictionarySetValue(setProps, memberType, uuidArray);
1012
1013    CFStringRef pathArrayName = 0;
1014    if (CFStringCompare(memberType, CFSTR(kAppleRAIDMembersKey), 0) == kCFCompareEqualTo) {
1015	pathArrayName = CFSTR("_member names_");
1016    }
1017    if (CFStringCompare(memberType, CFSTR(kAppleRAIDSparesKey), 0) == kCFCompareEqualTo) {
1018	pathArrayName = CFSTR("_spare names_");
1019    }
1020    if (!pathArrayName) return NULL;
1021
1022    CFMutableArrayRef pathArray = (CFMutableArrayRef)CFDictionaryGetValue(setProps, pathArrayName);
1023    if (!pathArray) {
1024	pathArray = (CFMutableArrayRef)CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1025	if (pathArray) CFDictionarySetValue(setProps, pathArrayName, pathArray);
1026    }
1027    if (!pathArray) return NULL;
1028
1029    CFArrayAppendValue(uuidArray, uuidString);
1030    CFArrayAppendValue(pathArray, partitionName);
1031
1032    CFRelease(uuidString);
1033
1034    // enable autorebuild if the set is not degraded and we are adding a spare
1035    if (CFStringCompare(memberType, CFSTR(kAppleRAIDSparesKey), 0) == kCFCompareEqualTo) {
1036	CFMutableStringRef status = (CFMutableStringRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDStatusKey));
1037	if (status) {
1038	    if (CFStringCompare(status, CFSTR(kAppleRAIDStatusOnline), 0) == kCFCompareEqualTo) {
1039		AppleRAIDModifySet(setProps, CFSTR(kAppleRAIDSetAutoRebuildKey), (void *)kCFBooleanTrue);
1040	    }
1041	}
1042    }
1043
1044    return (AppleRAIDMemberRef)uuidString;
1045}
1046
1047// ***************************************************************************************************
1048//
1049// set modification
1050//
1051// ***************************************************************************************************
1052
1053#include <sys/fcntl.h>
1054
1055static bool
1056writeHeader(CFMutableDictionaryRef memberProps, memberInfo_t * memberInfo)
1057{
1058    AppleRAIDHeaderV2 * header = calloc(1, kAppleRAIDHeaderSize);
1059    if (!header) return false;
1060
1061    strlcpy(header->raidSignature, kAppleRAIDSignature, sizeof(header->raidSignature));
1062    CFStringRef string;
1063    string = (CFStringRef)CFDictionaryGetValue(memberProps, CFSTR(kAppleRAIDSetUUIDKey));
1064    if (string) CFStringGetCString(string, header->raidUUID, 64, kCFStringEncodingUTF8);
1065    string = (CFStringRef)CFDictionaryGetValue(memberProps, CFSTR(kAppleRAIDMemberUUIDKey));
1066    if (string) CFStringGetCString(string, header->memberUUID, 64, kCFStringEncodingUTF8);
1067
1068    header->size = memberInfo->chunkCount * memberInfo->chunkSize;
1069    ByteSwapHeaderV2(header);
1070
1071    // strip any internal keys from header dictionary before writing to disk
1072    CFMutableDictionaryRef headerInfo = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, memberProps);
1073    if (!headerInfo) return false;
1074    CFIndex propCount = CFDictionaryGetCount(headerInfo);
1075    if (!propCount) return false;
1076    const void ** keys = calloc(propCount, sizeof(void *));
1077    if (!keys) return false;
1078    CFDictionaryGetKeysAndValues(headerInfo, keys, NULL);
1079    CFIndex i;
1080    for (i = 0; i < propCount; i++) {
1081	if (!CFStringHasPrefix(keys[i], CFSTR("AppleRAID-"))) {
1082	    CFDictionaryRemoveValue(headerInfo, keys[i]);
1083	}
1084    }
1085    CFDictionaryRemoveValue(headerInfo, CFSTR(kAppleRAIDSetQuickRebuildKey));	// redundant
1086    CFDictionaryRemoveValue(headerInfo, CFSTR(kAppleRAIDLVGExtentsKey));	// redundant
1087    CFDictionaryRemoveValue(headerInfo, CFSTR(kAppleRAIDLVGVolumeCountKey));	// redundant
1088    CFDictionaryRemoveValue(headerInfo, CFSTR(kAppleRAIDLVGFreeSpaceKey));	// redundant
1089
1090    CFDataRef setData = IOCFSerialize(headerInfo, kNilOptions);
1091    if (!setData) {
1092	IOLog1("AppleRAIDLib - serialize on memberProps failed\n");
1093	return false;
1094    }
1095    bcopy(CFDataGetBytePtr(setData), header->plist, CFDataGetLength(setData));
1096    CFRelease(headerInfo);
1097    CFRelease(setData);
1098
1099    int fd = open(memberInfo->devicePath, O_RDWR, 0);
1100    if (fd < 0) return false;
1101
1102    IOLog1("writeHeader %s, header offset = %llu.\n", memberInfo->devicePath, memberInfo->headerOffset);
1103
1104    off_t seek = lseek(fd, memberInfo->headerOffset, SEEK_SET);
1105    if (seek != memberInfo->headerOffset) goto ioerror;
1106
1107    int length = write(fd, header, kAppleRAIDHeaderSize);
1108    if (length < kAppleRAIDHeaderSize) goto ioerror;
1109
1110    close(fd);
1111    free(header);
1112    return true;
1113
1114ioerror:
1115    close(fd);
1116    free(header);
1117    return false;
1118}
1119
1120static CFDataRef
1121readHeader(memberInfo_t * memberInfo)
1122{
1123    CFDataRef headerData = NULL;
1124
1125    int fd = open(memberInfo->devicePath, O_RDONLY, 0);
1126    if (fd < 0) return NULL;
1127
1128    AppleRAIDHeaderV2 * header = calloc(1, kAppleRAIDHeaderSize);
1129    if (!header) goto error;
1130
1131//    IOLog1("readHeader %s, header offset = %llu.\n", memberInfo->devicePath, memberInfo->headerOffset);
1132
1133    off_t seek = lseek(fd, memberInfo->headerOffset, SEEK_SET);
1134    if (seek != memberInfo->headerOffset) goto error;
1135
1136    int length = read(fd, header, kAppleRAIDHeaderSize);
1137    if (length < kAppleRAIDHeaderSize) goto error;
1138
1139    if (!strncmp(header->raidSignature, kAppleRAIDSignature, sizeof(header->raidSignature))) {
1140	 headerData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)header, kAppleRAIDHeaderSize);
1141    }
1142
1143error:
1144    close(fd);
1145    if (header) free(header);
1146
1147    return headerData;
1148}
1149
1150static AppleRAIDPrimaryOnDisk *
1151initPrimaryMetaDataForMirror(memberInfo_t * memberInfo)
1152{
1153    if (!memberInfo->primaryMetaDataSize) return false;
1154
1155    void * bitmap = calloc(1, memberInfo->primaryMetaDataSize);
1156    if (!bitmap) return NULL;
1157
1158    // setup bitmap using extents
1159    AppleRAIDPrimaryOnDisk * map = bitmap;
1160    strlcpy(map->priMagic, kAppleRAIDPrimaryMagic, sizeof(map->priMagic));
1161    map->priSize = memberInfo->primaryMetaDataSize;
1162    map->priType = kAppleRAIDPrimaryExtents;
1163    map->priSequenceNumber = 0;  // set by caller
1164    map->pri.extentCount = 1;
1165    map->priUsed = sizeof(AppleRAIDPrimaryOnDisk);
1166
1167    AppleRAIDExtentOnDisk * extent = (AppleRAIDExtentOnDisk *)(map + 1);
1168    extent->extentByteOffset = 0;
1169    extent->extentByteCount = memberInfo->chunkCount * memberInfo->chunkSize;
1170    map->priUsed += sizeof(AppleRAIDExtentOnDisk);
1171
1172    memberInfo->primaryData = bitmap;
1173
1174    return bitmap;
1175}
1176
1177static AppleRAIDPrimaryOnDisk *
1178initPrimaryMetaDataForLVG(memberInfo_t * memberInfo)
1179{
1180    if (!memberInfo->primaryMetaDataSize) return false;
1181
1182    void * primary = calloc(1, memberInfo->primaryMetaDataSize);
1183    if (!primary) return NULL;
1184
1185    // setup primary header for LVG
1186    AppleRAIDPrimaryOnDisk * header = primary;
1187    strlcpy(header->priMagic, kAppleRAIDPrimaryMagic, sizeof(header->priMagic));
1188    header->priSize = memberInfo->primaryMetaDataSize;
1189    header->priType = kAppleRAIDPrimaryLVG;
1190    header->priSequenceNumber = 0;  // set by caller
1191    header->pri.volumeCount = 0;
1192
1193    header->priUsed = sizeof(AppleRAIDPrimaryOnDisk);
1194
1195    memberInfo->primaryData = primary;
1196
1197    return primary;
1198}
1199
1200static CFMutableDictionaryRef initLogicalVolumeProps(CFStringRef lvgUUIDString, CFStringRef volumeType, UInt64 size,
1201						     CFStringRef location, CFNumberRef sequenceNumber, CFDataRef extentData);
1202static AppleLVMVolumeOnDisk * buildLVMetaDataBlock(CFMutableDictionaryRef lvProps, CFDataRef extentData);
1203
1204static void *
1205initSecondaryMetaDataForLVG(memberInfo_t * memberInfo, CFMutableDictionaryRef setProps)
1206{
1207    CFMutableDictionaryRef lvProps = 0;
1208    AppleLVMVolumeOnDisk * lvData = 0;
1209    void * secondary = 0;
1210
1211    if (!memberInfo->secondaryMetaDataSize || !setProps) return NULL;
1212
1213    CFStringRef lvgUUIDString = CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSetUUIDKey));
1214    if (!lvgUUIDString) return NULL;
1215    const void * sequenceProp = CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSequenceNumberKey));
1216    if (!sequenceProp) return NULL;
1217    CFStringRef volumeType = CFSTR(kAppleLVMVolumeTypeMaster);
1218    UInt64 volumeSize =  memberInfo->secondaryMetaDataSize;
1219
1220    // the first logical volume is used to hold the logical volume
1221    // entries on its disk, since the lvg needs to be up before you
1222    // can add a logical volume it is special.  it needs to
1223    // work when disks are missing so it is relative to the member
1224    // instead of the logical volume group.  it is not listed in
1225    // the TOC but assumed to be the first entry in secondary
1226    // metadata area of the disk
1227
1228    AppleRAIDExtentOnDisk extent;
1229    extent.extentByteOffset = memberInfo->chunkCount * memberInfo->chunkSize - memberInfo->secondaryMetaDataSize;
1230    extent.extentByteCount = memberInfo->secondaryMetaDataSize;
1231
1232    CFDataRef extentData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)&extent, sizeof(AppleRAIDExtentOnDisk));
1233    if (!extentData) goto error;
1234
1235    lvProps = initLogicalVolumeProps(lvgUUIDString, volumeType, volumeSize, CFSTR("meta"), sequenceProp, extentData);
1236    if (!lvProps) goto error;
1237    lvData = buildLVMetaDataBlock(lvProps, extentData);
1238    if (!lvData) goto error;
1239
1240    secondary = calloc(1, memberInfo->secondaryMetaDataSize);
1241    if (!secondary) goto error;
1242
1243    bcopy(lvData, secondary, lvData->lvHeaderSize);
1244
1245    if (lvProps) CFRelease(lvProps);
1246    if (lvData) free(lvData);
1247    if (extentData) CFRelease(extentData);
1248
1249    memberInfo->secondaryData = secondary;
1250
1251    return secondary;
1252
1253error:
1254    if (lvProps) CFRelease(lvProps);
1255    if (lvData) free(lvData);
1256    if (extentData) CFRelease(extentData);
1257    if (secondary) free(secondary);
1258
1259    return NULL;
1260}
1261
1262static bool
1263writePrimaryMetaData(memberInfo_t * memberInfo)
1264{
1265    if (!memberInfo->primaryData) return false;
1266    if (!memberInfo->primaryMetaDataSize) return false;
1267
1268#if defined(__LITTLE_ENDIAN__)
1269    AppleRAIDPrimaryOnDisk * header = memberInfo->primaryData;
1270    if (header->priType == kAppleRAIDPrimaryExtents) {
1271	int i;
1272	AppleRAIDExtentOnDisk * extent = (AppleRAIDExtentOnDisk *)(header + 1);
1273	for (i=0; i < header->pri.extentCount; i++) {
1274	    ByteSwapExtent(extent + i);
1275	}
1276    }
1277    ByteSwapPrimaryHeader(header);
1278#endif
1279
1280    int fd = open(memberInfo->devicePath, O_RDWR, 0);
1281    if (fd < 0) return false;
1282
1283    off_t metaDataOffset = memberInfo->chunkCount * memberInfo->chunkSize;
1284
1285    IOLog1("writePrimary %s, meta data offset = %llu.\n", memberInfo->devicePath, metaDataOffset);
1286
1287    off_t seek = lseek(fd, metaDataOffset, SEEK_SET);
1288    if (seek != metaDataOffset) goto error;
1289
1290    int length = write(fd, memberInfo->primaryData, memberInfo->primaryMetaDataSize);
1291
1292    if (length < memberInfo->primaryMetaDataSize) goto error;
1293
1294    close(fd);
1295    return true;
1296
1297error:
1298    close(fd);
1299    return false;
1300}
1301
1302#if 0
1303static AppleRAIDPrimaryOnDisk *
1304readPrimaryMetaData(memberInfo_t * memberInfo)
1305{
1306    UInt64 primaryOffset = memberInfo->chunkSize * memberInfo->chunkCount;
1307    UInt64 primarySize = memberInfo->primaryMetaDataSize;
1308
1309    if (memberInfo->primaryData) free(memberInfo->primaryData);
1310    memberInfo->primaryData = NULL;
1311
1312    AppleRAIDPrimaryOnDisk * primary = calloc(1, primarySize);
1313    if (!primary) return NULL;
1314
1315    int fd = open(memberInfo->devicePath, O_RDONLY, 0);
1316    if (fd < 0) return NULL;
1317
1318    IOLog1("readPrimary %s, offset = %llu, size = %llu.\n", memberInfo->devicePath, primaryOffset, primarySize);
1319
1320    off_t seek = lseek(fd, primaryOffset, SEEK_SET);
1321    if (seek != primaryOffset) goto error;
1322
1323    int length = read(fd, primary, primarySize);
1324
1325    if (length < primarySize) goto error;
1326
1327    if (!strncmp(primary->priMagic, kAppleRAIDPrimaryMagic, sizeof(primary->priMagic))) {
1328	memberInfo->primaryData = primary;
1329    } else {
1330	IOLog1("readPrimary, found bad magic on %s.\n", memberInfo->devicePath);
1331    }
1332
1333error:
1334    close(fd);
1335
1336#if defined(__LITTLE_ENDIAN__)
1337    AppleRAIDPrimaryOnDisk * header = memberInfo->primaryData;
1338    ByteSwapPrimaryHeader(header);
1339    if (header->priType == kAppleRAIDPrimaryExtents) {
1340	int i;
1341	AppleRAIDExtentOnDisk * extent = (AppleRAIDExtentOnDisk *)(header + 1);
1342	for (i=0; i < header->pri.extentCount; i++) {
1343	    ByteSwapExtent(extent + i);
1344	}
1345    }
1346#endif
1347
1348    return memberInfo->primaryData;
1349}
1350#endif
1351
1352// XXX instead of allocating a huge chunk of a memory and zeroing, the code
1353// could write a smaller chunk over and over same for primary data
1354
1355static bool
1356writeSecondaryMetaData(memberInfo_t * memberInfo)
1357{
1358    if (!memberInfo->secondaryData) return false;
1359    if (!memberInfo->secondaryMetaDataSize) return false;
1360
1361    AppleLVMVolumeOnDisk * header = memberInfo->secondaryData;
1362    AppleRAIDExtentOnDisk * extent = (AppleRAIDExtentOnDisk *)((char *)header + header->lvExtentsStart);
1363    // since this can only be called when creating a LVG, we know there is only one extent we need to swap
1364    assert(header->lvExtentsCount == 1);
1365    ByteSwapExtent(extent);
1366    ByteSwapLVMVolumeHeader(header);
1367
1368    int fd = open(memberInfo->devicePath, O_RDWR, 0);
1369    if (fd < 0) return false;
1370
1371    off_t metaDataOffset = memberInfo->chunkCount * memberInfo->chunkSize - memberInfo->secondaryMetaDataSize;
1372
1373    IOLog1("writeSecondary %s, meta data offset = %llu, size = %llu.\n",
1374	   memberInfo->devicePath, metaDataOffset, memberInfo->secondaryMetaDataSize);
1375
1376    off_t seek = lseek(fd, metaDataOffset, SEEK_SET);
1377    if (seek != metaDataOffset) goto error;
1378
1379    int length = write(fd, memberInfo->secondaryData, memberInfo->secondaryMetaDataSize);
1380
1381    if (length < memberInfo->secondaryMetaDataSize) goto error;
1382
1383    close(fd);
1384    return true;
1385
1386error:
1387    close(fd);
1388    return false;
1389}
1390
1391static bool
1392updateLiveSet(CFMutableDictionaryRef setProps)
1393{
1394    CFStringRef setUUIDString = CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSetUUIDKey));
1395
1396    // strip out any properties that haven't changed
1397    CFMutableDictionaryRef currentSet = AppleRAIDGetSetProperties(setUUIDString);
1398    CFMutableDictionaryRef updatedInfo = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, setProps);
1399    if (!updatedInfo) return false;
1400
1401    CFIndex propCount = CFDictionaryGetCount(updatedInfo);
1402    const void **newKeys = (const void **)malloc(2 * propCount * sizeof(void *));
1403    const void **newValues = newKeys + propCount;
1404    CFDictionaryGetKeysAndValues(updatedInfo, newKeys, newValues);
1405
1406    CFIndex i;
1407    for (i = 0; i < propCount; i++) {
1408	const void * oldValue = 0;
1409	if (CFDictionaryGetValueIfPresent(currentSet, newKeys[i], &oldValue)) {
1410	    if (CFEqual(newValues[i], oldValue)) {
1411		CFDictionaryRemoveValue(updatedInfo, newKeys[i]);
1412	    }
1413	}
1414    }
1415    propCount = CFDictionaryGetCount(updatedInfo);
1416
1417    // hm, nothing changed?
1418    if (!propCount) {
1419	IOLog1("AppleRAID - updateLiveSet: nothing was changed in the set?\n");
1420	return false;
1421    }
1422
1423    // put the set uuid back in
1424    CFDictionarySetValue(updatedInfo, CFSTR(kAppleRAIDSetUUIDKey), setUUIDString);
1425
1426    // put the sequence number back in (in case the set changed underneath us)
1427    const void * seqNum = CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSequenceNumberKey));
1428    if (!seqNum) return false;
1429    CFDictionarySetValue(updatedInfo, CFSTR(kAppleRAIDSequenceNumberKey), seqNum);
1430
1431    // Serialize what is left
1432    CFDataRef setData = IOCFSerialize(updatedInfo, kNilOptions);
1433    if (!setData) {
1434	IOLog1("AppleRAID - updateLiveSet failed serializing on updatedInfo.\n");
1435	return false;
1436    }
1437
1438    io_connect_t raidControllerPort = AppleRAIDOpenConnection();
1439    if (!raidControllerPort) {
1440	IOLog1("AppleRAID - updateLiveSet - failed connecting to raid controller object?\n");
1441	return false;
1442    }
1443
1444    kern_return_t 	kr;
1445    char *		buffer = (char *)CFDataGetBytePtr(setData);
1446    size_t		bufferSize = CFDataGetLength(setData);
1447    char		updateData[0x1000];
1448    size_t		updateDataSize = sizeof(updateData);
1449
1450    if (!buffer) return false;
1451
1452    IOLog1("update set changes = %s\n", buffer);
1453
1454    kr = IOConnectCallStructMethod(raidControllerPort,		// an io_connect_t returned from IOServiceOpen().
1455				   kAppleRAIDUpdateSet,		// an index to the function in the Kernel.
1456				   buffer,			// input
1457				   bufferSize,			// input size
1458				   updateData,			// output
1459				   &updateDataSize);		// output size (in/out)
1460
1461    if (kr != KERN_SUCCESS) {
1462	IOLog1("AppleRAID - updateLiveSet failed with %x calling client.\n", kr);
1463	AppleRAIDCloseConnection();
1464	return false;
1465    }
1466
1467    // get back the updated sequence number
1468    seqNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, (UInt32 *)updateData);
1469    if (!seqNum) return false;
1470    CFDictionarySetValue(setProps, CFSTR(kAppleRAIDSequenceNumberKey), seqNum);
1471
1472    CFRelease(setData);
1473    CFRelease(updatedInfo);
1474
1475    AppleRAIDCloseConnection();
1476
1477    return true;
1478}
1479
1480// for each member initalize the following member specific values
1481//	kAppleRAIDMemberTypeKey - spare or member
1482//	kAppleRAIDMemberUUIDKey
1483//	kAppleRAIDMemberIndexKey - index in set  (9999 for spare)
1484//	kAppleRAIDChunkCountKey - size of this member
1485
1486static bool
1487createNewMembers(CFMutableDictionaryRef setProps, memberInfo_t ** memberInfo,
1488		 CFIndex memberCount, CFIndex spareCount,
1489		 CFIndex newMemberCount, CFIndex newSpareCount)
1490{
1491    if (!memberInfo) return false;
1492
1493    CFStringRef raidType = (CFStringRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDLevelNameKey));
1494    bool isLVG = CFEqual(raidType, CFSTR(kAppleRAIDLevelNameLVG));
1495    bool isMirror = CFEqual(raidType, CFSTR(kAppleRAIDLevelNameMirror));
1496
1497    UInt32 i;
1498    for (i = 0; i < newMemberCount + newSpareCount; i++) {
1499
1500	// whole raw disks are not supported
1501	if ((memberInfo[i]->isWhole) && (!memberInfo[i]->isRAID)) return false;
1502
1503	CFStringRef typeString= 0, uuidString = 0;
1504	CFNumberRef index = 0, count = 0;
1505	if (i < newMemberCount) {
1506
1507	    typeString = CFSTR(kAppleRAIDMembersKey);
1508
1509	    UInt32 memberIndex = i + memberCount;
1510	    index = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &memberIndex);
1511
1512	    CFMutableArrayRef uuidArray = (CFMutableArrayRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDMembersKey));
1513	    if (!uuidArray) return false;
1514	    uuidString = (CFStringRef)CFArrayGetValueAtIndex(uuidArray, memberIndex);
1515
1516	} else {
1517
1518	    typeString = CFSTR(kAppleRAIDSparesKey);
1519
1520	    UInt32 spareIndex = kAppleRAIDDummySpareIndex;
1521	    index = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &spareIndex);
1522
1523	    CFMutableArrayRef uuidArray = (CFMutableArrayRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSparesKey));
1524	    if (!uuidArray) return false;
1525	    spareIndex = i - newMemberCount + spareCount;
1526	    uuidString = (CFStringRef)CFArrayGetValueAtIndex(uuidArray, spareIndex);
1527	}
1528
1529	count = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &memberInfo[i]->chunkCount);
1530
1531	if (typeString && index && uuidString && count) {
1532	    CFDictionarySetValue(setProps, CFSTR(kAppleRAIDMemberTypeKey), typeString);
1533	    CFDictionarySetValue(setProps, CFSTR(kAppleRAIDMemberIndexKey), index);
1534	    CFDictionarySetValue(setProps, CFSTR(kAppleRAIDMemberUUIDKey), uuidString);
1535	    CFDictionarySetValue(setProps, CFSTR(kAppleRAIDChunkCountKey), count);
1536
1537	    CFRelease(index);
1538	    CFRelease(count);
1539	} else {
1540	    return false;
1541	}
1542
1543	if (memberInfo[i]->primaryMetaDataSize) {
1544	    // layout the primary meta data
1545	    AppleRAIDPrimaryOnDisk * primary = NULL;
1546	    if (isLVG)    primary = initPrimaryMetaDataForLVG(memberInfo[i]);
1547	    if (isMirror) primary = initPrimaryMetaDataForMirror(memberInfo[i]);
1548	    if (!primary) {
1549		IOLog1("AppleRAIDUpdateSet - failed to create the primary metadata for partition \"%s\"\n", memberInfo[i]->diskName);
1550		return false;
1551	    }
1552
1553	    // update the sequence number
1554	    const void * number = CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSequenceNumberKey));
1555	    if (!number) return false;
1556	    if (!CFNumberGetValue(number, kCFNumberSInt32Type, &primary->priSequenceNumber)) return false;
1557
1558	    // set the amount used in the in the raid header
1559	    UInt64 usedSize = memberInfo[i]->primaryData->priUsed;
1560	    CFNumberRef meta1 = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &usedSize);
1561	    if (!meta1) return false;
1562	    CFDictionarySetValue(setProps, CFSTR(kAppleRAIDPrimaryMetaDataUsedKey), meta1);
1563	    CFRelease(meta1);
1564	}
1565
1566	if (memberInfo[i]->secondaryMetaDataSize) {
1567	    void * secondary = NULL;
1568	    if (isLVG) secondary = initSecondaryMetaDataForLVG(memberInfo[i], setProps);
1569	    if (!secondary) {
1570		IOLog1("AppleRAIDUpdateSet - failed to create the secondary metadata for partition \"%s\"\n", memberInfo[i]->diskName);
1571		return false;
1572	    }
1573
1574	    CFNumberRef meta2 = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &memberInfo[i]->secondaryMetaDataSize);
1575	    if (!meta2) return false;
1576	    CFDictionarySetValue(setProps, CFSTR(kAppleRAIDSecondaryMetaDataSizeKey), meta2);
1577	    CFRelease(meta2);
1578	}
1579
1580	CFStringRef partitionName = CFStringCreateWithCString(kCFAllocatorDefault, memberInfo[i]->diskName, kCFStringEncodingUTF8);
1581	if (!partitionName) return false;
1582	bool success = AppleRAIDRemoveHeaders(partitionName);
1583	if (!success) {
1584	    IOLog1("AppleRAIDUpdateSet - there was a problem erasing the raid headers on partition \"%s\"\n", memberInfo[i]->diskName);
1585	    return false;
1586	}
1587	CFRelease(partitionName);
1588
1589	if (memberInfo[i]->secondaryMetaDataSize && !writeSecondaryMetaData(memberInfo[i])) {
1590	    IOLog1("AppleRAIDUpdateSet - failed while writing secondary metadata to partition \"%s\"\n", memberInfo[i]->diskName);
1591	    return false;
1592	}
1593
1594	if (memberInfo[i]->primaryMetaDataSize && !writePrimaryMetaData(memberInfo[i])) {
1595	    IOLog1("AppleRAIDUpdateSet - failed while writing primary metadata to partition \"%s\"\n", memberInfo[i]->diskName);
1596	    return false;
1597	}
1598
1599	if (!writeHeader(setProps, memberInfo[i])) {
1600	    IOLog1("AppleRAIDUpdateSet - failed while writing RAID header to partition \"%s\"\n", memberInfo[i]->diskName);
1601	    return false;
1602	}
1603
1604	//  if this is a stacked set then force the set to read the new headers
1605	if (memberInfo[i]->isRAID) {
1606
1607	    CFMutableDictionaryRef updateInfo = CFDictionaryCreateMutable(kCFAllocatorDefault,
1608									  3,					// count
1609									  &kCFTypeDictionaryKeyCallBacks,
1610									  &kCFTypeDictionaryValueCallBacks);
1611	    if (!updateInfo) return false;
1612	    CFDictionarySetValue(updateInfo, CFSTR(kAppleRAIDSetUUIDKey), memberInfo[i]->uuidString);
1613	    UInt32 zero = 0;
1614	    CFNumberRef seqNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &zero);
1615	    if (!seqNum) return false;
1616	    CFDictionarySetValue(updateInfo, CFSTR(kAppleRAIDSequenceNumberKey), seqNum);
1617
1618	    UInt32 subCommand = kAppleRAIDUpdateResetSet;
1619	    CFNumberRef updateSubCommand = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &subCommand);
1620	    CFDictionarySetValue(updateInfo, CFSTR("_update command_"), updateSubCommand);
1621
1622	    updateLiveSet(updateInfo);
1623	}
1624    }
1625
1626    return true;
1627}
1628
1629static UInt64 calculateBitMapSize(UInt64 partitionSize, UInt64 chunkSize, UInt64 * remainingBytes);
1630
1631AppleRAIDSetRef
1632AppleRAIDUpdateSet(CFMutableDictionaryRef setProps)
1633{
1634    CFStringRef setUUIDString = CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSetUUIDKey));
1635    CFRetain(setUUIDString);
1636    memberInfo_t ** memberInfo = 0;
1637
1638#if DEBUG
1639    CFShow(setUUIDString);
1640#endif
1641
1642    // pull out the fluff
1643    CFIndex memberCount = 0, spareCount = 0;
1644    CFIndex newMemberCount = 0, newSpareCount = 0;
1645
1646    CFMutableArrayRef newMemberNames = (CFMutableArrayRef)CFDictionaryGetValue(setProps, CFSTR("_member names_"));
1647    if (newMemberNames) {
1648	CFRetain(newMemberNames);
1649	CFDictionaryRemoveValue(setProps, CFSTR("_member names_"));
1650	newMemberCount = CFArrayGetCount(newMemberNames);
1651    }
1652
1653    CFMutableArrayRef newSpareNames = (CFMutableArrayRef)CFDictionaryGetValue(setProps, CFSTR("_spare names_"));
1654    if (newSpareNames) {
1655	CFRetain(newSpareNames);
1656	CFDictionaryRemoveValue(setProps, CFSTR("_spare names_"));
1657	newSpareCount = CFArrayGetCount(newSpareNames);
1658    }
1659
1660
1661    // if the raid set has status it is "live", get it's current member/spare counts
1662    bool liveSet = CFDictionaryContainsKey(setProps, CFSTR(kAppleRAIDStatusKey));  // this only works once
1663    if (liveSet) {
1664	CFDictionaryRemoveValue(setProps, CFSTR(kAppleRAIDStatusKey));
1665
1666	CFMutableArrayRef tempMembers = (CFMutableArrayRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDMembersKey));
1667	if (tempMembers) {
1668	    memberCount = CFArrayGetCount(tempMembers) - newMemberCount;
1669	}
1670	CFMutableArrayRef tempSpares = (CFMutableArrayRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSparesKey));
1671	if (tempSpares) {
1672	    spareCount = CFArrayGetCount(tempSpares) - newSpareCount;
1673	}
1674    }
1675
1676    // get info for new members and/or spares
1677    if (newSpareCount || newMemberCount) {
1678
1679	memberInfo = calloc(newMemberCount + newSpareCount, sizeof(memberInfo_t *));
1680	if (!memberInfo) return NULL;
1681
1682	UInt32 i;
1683	for (i = 0; i < newMemberCount + newSpareCount; i++) {
1684	    CFStringRef diskName;
1685	    if (i < newMemberCount) {
1686		diskName = (CFStringRef)CFArrayGetValueAtIndex(newMemberNames, i);
1687	    } else {
1688		diskName = (CFStringRef)CFArrayGetValueAtIndex(newSpareNames, i - newMemberCount);
1689	    }
1690
1691	    memberInfo[i] = getMemberInfo(diskName);
1692	    if (!memberInfo[i]) return NULL;
1693#ifdef DEBUG
1694	    if (memberInfo[i]) {
1695		IOLog1("\t%s: regName = \"%s\" size = %lld block size = %lld whole = %s raid = %s uuid = %p\n",
1696		       memberInfo[i]->diskName, memberInfo[i]->regName, memberInfo[i]->size, memberInfo[i]->blockSize,
1697		       memberInfo[i]->isWhole?"true":"false", memberInfo[i]->isRAID?"true":"false", memberInfo[i]->uuidString);
1698	    }
1699#endif
1700	}
1701	if (newMemberNames) CFRelease(newMemberNames);
1702	if (newSpareNames) CFRelease(newSpareNames);
1703
1704	bool sizesCanVary = (CFBooleanRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSizesCanVaryKey)) == kCFBooleanTrue;
1705	bool quickRebuild = (CFBooleanRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSetQuickRebuildKey)) == kCFBooleanTrue;
1706	CFStringRef raidType = (CFStringRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDLevelNameKey));
1707	bool isLVG = CFEqual(raidType, CFSTR(kAppleRAIDLevelNameLVG));
1708
1709	UInt64 metaDataSize = 0;
1710	if (quickRebuild) {
1711	    if (liveSet) {
1712		// XXX this is the used size, not whole size
1713		CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDPrimaryMetaDataUsedKey));
1714		if (!number || !CFNumberGetValue(number, kCFNumberSInt64Type, &metaDataSize) || !metaDataSize) {
1715		    printf("AppleRAID: Failed to find the size of the mirror quick rebuild bitmap.\n");
1716		    return NULL;
1717		}
1718	    }
1719	}
1720
1721	// determine each partition's chunk count
1722	UInt64 chunkSize = 0;
1723	CFNumberRef number;
1724	number = (CFNumberRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDChunkSizeKey));
1725	if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &chunkSize);
1726	if (!chunkSize) return NULL;
1727
1728	// find the smallest member (or spare)
1729	UInt64 smallestSize = 0;
1730	if (liveSet) {
1731	    // calculate the minimum required size for a member partition in this set
1732	    UInt64 chunkCount = 0;
1733	    CFNumberRef number;
1734	    number = (CFNumberRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDChunkCountKey));
1735	    if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &chunkCount);
1736
1737	    if (!chunkCount) return NULL;
1738
1739	    smallestSize = chunkCount * chunkSize + metaDataSize + (UInt64)kAppleRAIDHeaderSize;
1740	} else {
1741	    smallestSize = memberInfo[0]->size;
1742	}
1743	if (!sizesCanVary) {
1744	    for (i = 0; i < newMemberCount + newSpareCount; i++) {
1745		if (liveSet) {
1746		    if (memberInfo[i]->size < smallestSize) {
1747			IOLog1("AppleRAIDUpdateSet() new member is too small to add to set.\n");
1748			return NULL;
1749		    }
1750		} else {
1751		    if (memberInfo[i]->size < smallestSize) smallestSize = memberInfo[i]->size;
1752		}
1753	    }
1754	    IOLog1("smallest member size %lld\n", smallestSize);
1755	}
1756
1757	//  if quick rebuilding then factor in the required meta data size
1758	if (quickRebuild && !metaDataSize) {
1759
1760	    metaDataSize = calculateBitMapSize(smallestSize, chunkSize, NULL);
1761	    IOLog1("quick rebuild bit map size = %lld @ offset %lld\n", metaDataSize,
1762		   (ARHEADER_OFFSET(smallestSize) - metaDataSize) / chunkSize);
1763	}
1764
1765	if (isLVG) metaDataSize = 0x100000;  // XXXTOC start with a meg
1766
1767	for (i = 0; i < newMemberCount + newSpareCount; i++) {
1768
1769	    memberInfo[i]->chunkSize = chunkSize;
1770	    memberInfo[i]->primaryMetaDataSize = metaDataSize;
1771
1772	    // XXXTOC start with 4 megs which gives us 1024 min size volumes entries
1773	    // should be able calculate a better size based on the member size
1774	    if (isLVG) memberInfo[i]->secondaryMetaDataSize = 0x400000;
1775
1776	    if (sizesCanVary) {
1777		memberInfo[i]->chunkCount = (memberInfo[i]->headerOffset - metaDataSize) / chunkSize;
1778	    } else {
1779		memberInfo[i]->chunkCount = (ARHEADER_OFFSET(smallestSize) - metaDataSize) / chunkSize;
1780	    }
1781	}
1782    }
1783
1784    // warn controller of set change prior to adding new members
1785    // add give the set a chance to reject anything it does not like
1786
1787    if (liveSet) {
1788	if (!updateLiveSet(setProps)) return NULL;
1789    }
1790
1791    // write out headers on new members/spares
1792
1793    if (newSpareCount || newMemberCount) {
1794	if (!createNewMembers(setProps, memberInfo, memberCount,
1795			      spareCount, newMemberCount, newSpareCount)) return NULL;
1796    }
1797
1798    if (newSpareCount || newMemberCount) {
1799	UInt32 i;
1800	for (i=0; i < newSpareCount + newMemberCount; i++) {
1801	    freeMemberInfo(memberInfo[i]);
1802	}
1803	free(memberInfo);
1804    }
1805
1806    return setUUIDString;
1807}
1808
1809// ***************************************************************************************************
1810//
1811// set and member deletion
1812//
1813// ***************************************************************************************************
1814
1815bool
1816AppleRAIDRemoveHeaders(CFStringRef partitionName)
1817{
1818    memberInfo_t * memberInfo = getMemberInfo(partitionName);
1819    if (!memberInfo) return false;
1820
1821    // look for block zero header (old raid)
1822
1823    AppleRAIDHeaderV2 * header = calloc(1, kAppleRAIDHeaderSize);
1824    if (!header) return false;
1825
1826    int fd = open(memberInfo->devicePath, O_RDWR, 0);
1827    if (fd < 0) return false;
1828
1829    // look for v1 style header
1830    UInt64 headerOffset = 0;
1831    {
1832	IOLog2("AppleRAIDRemoveHeaders %s, scaning header offset = %llu.\n", memberInfo->devicePath, headerOffset);
1833
1834	off_t seek = lseek(fd, headerOffset, SEEK_SET);
1835	if (seek != headerOffset) return false;
1836
1837	int length = read(fd, header, kAppleRAIDHeaderSize);
1838	if (length < kAppleRAIDHeaderSize) return false;
1839
1840	if (!strncmp(header->raidSignature, kAppleRAIDSignature, sizeof(header->raidSignature))) {
1841	    IOLog1("AppleRAIDRemoveHeaders %s, found ARv1 header at offset = %llu.\n", memberInfo->devicePath, headerOffset);
1842
1843	    bzero(header, kAppleRAIDHeaderSize);
1844
1845	    seek = lseek(fd, headerOffset, SEEK_SET);
1846	    if (seek != headerOffset) return false;
1847
1848	    length = write(fd, header, kAppleRAIDHeaderSize);
1849	    if (length < kAppleRAIDHeaderSize) return false;
1850	}
1851    }
1852
1853    // scan for nested headers
1854    headerOffset = ARHEADER_OFFSET(memberInfo->size);
1855    int count = 5;
1856    while (headerOffset && count) {
1857	IOLog2("AppleRAIDRemoveHeaders %s, scanning header offset = %llu.\n", memberInfo->devicePath, headerOffset);
1858
1859	off_t seek = lseek(fd, headerOffset, SEEK_SET);
1860	if (seek != headerOffset) break;
1861
1862	int length = read(fd, header, kAppleRAIDHeaderSize);
1863	if (length < kAppleRAIDHeaderSize) break;
1864
1865	ByteSwapHeaderV2(header);
1866
1867	if (!strncmp(header->raidSignature, kAppleRAIDSignature, sizeof(header->raidSignature))) {
1868	    IOLog1("AppleRAIDRemoveHeaders %s, found ARv2 header at offset = %llu.\n", memberInfo->devicePath, headerOffset);
1869
1870	    UInt64 memberSize = header->size;
1871
1872	    bzero(header, kAppleRAIDHeaderSize);
1873
1874	    seek = lseek(fd, headerOffset, SEEK_SET);
1875	    if (seek != headerOffset) break;
1876
1877	    length = write(fd, header, kAppleRAIDHeaderSize);
1878	    if (length < kAppleRAIDHeaderSize) break;
1879
1880	    headerOffset = (memberSize < headerOffset) ? ARHEADER_OFFSET(memberSize) : 0;
1881
1882	} else {
1883
1884	    headerOffset = 0;
1885	}
1886	count--;
1887    }
1888
1889    close(fd);
1890    freeMemberInfo(memberInfo);
1891
1892    return headerOffset == 0;
1893}
1894
1895
1896bool
1897AppleRAIDRemoveMember(CFMutableDictionaryRef setProps, AppleRAIDMemberRef member)
1898{
1899    // make sure we support this operation
1900    UInt32 version;
1901    CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDHeaderVersionKey));
1902    if (!number || !CFNumberGetValue(number, kCFNumberSInt32Type, &version) || version < 0x00020000) {
1903	printf("AppleRAID: This operation is not supported on earlier RAID set revisions.\n");
1904	return NULL;
1905    }
1906
1907    // find the member or spare
1908    CFMutableArrayRef uuidArray = (CFMutableArrayRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDMembersKey));
1909    CFMutableArrayRef uuidArray2 = 0;
1910    if (!uuidArray) return NULL;
1911    CFIndex count = 0;
1912    CFIndex index;
1913
1914again:
1915
1916    count = CFArrayGetCount(uuidArray);
1917    for (index = 0; index < count; index++) {
1918	CFStringRef uuidString = (CFStringRef)CFArrayGetValueAtIndex(uuidArray, index);
1919	if (CFStringCompare(member, uuidString, 0) == kCFCompareEqualTo) {
1920	    CFArraySetValueAtIndex(uuidArray, index, CFSTR(kAppleRAIDDeletedUUID));
1921	    return true;
1922	}
1923    }
1924
1925    // same for spares array
1926    if (!uuidArray2) {
1927	uuidArray2 = (CFMutableArrayRef)CFDictionaryGetValue(setProps, CFSTR(kAppleRAIDSparesKey));
1928	if (uuidArray2 && CFArrayGetCount(uuidArray2)) {
1929	    uuidArray = uuidArray2;
1930	    goto again;
1931	}
1932    }
1933
1934    return false;
1935}
1936
1937
1938bool
1939AppleRAIDDestroySet(AppleRAIDSetRef setName)
1940{
1941    CFMutableDictionaryRef setProps = AppleRAIDGetSetProperties(setName);
1942    if (!setProps) return false;
1943
1944    UInt32 subCommand = kAppleRAIDUpdateDestroySet;
1945    CFNumberRef destroySubCommand = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &subCommand);
1946    CFDictionarySetValue(setProps, CFSTR("_update command_"), destroySubCommand);
1947
1948    if (!updateLiveSet(setProps)) return false;
1949
1950    CFRelease(setProps);
1951
1952    return true;
1953}
1954
1955
1956#define kAppleRAIDMinBitMapBytesPerBit	(512 * 1024)		// min bytes allowed to be represented by one bit
1957#define kAppleRAIDMaxBitMapBytesPerBit	(32 * 1024 * 1024) 	// max bytes allowed to be represented by one bit
1958
1959#define kAppleRAIDBitMapPageSize	(4 * 1024)		// the "offical" raid page size
1960#define kAppleRAIDMinBitMapSize		(32 * 4 * 1024)		// 128k
1961
1962// the largest bitmap for a 0x10000000000000000 volume is 4GB
1963
1964// if remainingBytes is set, we are trying to fit the bitmap into the partition, the bitmap covers the data section of the partition.
1965// if remainingBytes is not set, we are trying to find the size of a bitmap to cover the whole partition.
1966// in either case this function returns the size of the bitmap
1967
1968// XXX this code is too agressive in bumping the number of bytes per bit
1969
1970static UInt64 calculateBitMapSize(UInt64 partitionSize, UInt64 chunkSize, UInt64 * remainingBytes)
1971{
1972    UInt64 bytesPerBit = kAppleRAIDMinBitMapBytesPerBit;
1973    UInt64 bitMapSize = kAppleRAIDMinBitMapSize;
1974    UInt64 bitsNeeded;
1975
1976    // adjust bytes per bit until we have a reasonable sized bitmap
1977
1978    if (remainingBytes) {
1979
1980	UInt64 availableBytes = ARHEADER_OFFSET(partitionSize) - sizeof(AppleRAIDPrimaryOnDisk);
1981	bitsNeeded = (availableBytes - bitMapSize) / bytesPerBit;
1982	bitsNeeded += (availableBytes - bitMapSize) % bytesPerBit ? 1 : 0;
1983	while (bitsNeeded > (bitMapSize * 8)) {
1984
1985	    bytesPerBit *= 2;
1986	    if (bytesPerBit > kAppleRAIDMaxBitMapBytesPerBit) {
1987		bytesPerBit = kAppleRAIDMinBitMapBytesPerBit;
1988		bitMapSize += kAppleRAIDMinBitMapSize;
1989	    }
1990	    bitsNeeded = (availableBytes - bitMapSize) / bytesPerBit;
1991	    bitsNeeded += (availableBytes - bitMapSize) % bytesPerBit ? 1 : 0;
1992	}
1993
1994	*remainingBytes = (availableBytes - bitMapSize) / chunkSize * chunkSize;
1995
1996    } else {
1997
1998	bitsNeeded = partitionSize / bytesPerBit;
1999	bitsNeeded += partitionSize % bytesPerBit ? 1 : 0;
2000	while (bitsNeeded > (bitMapSize * 8)) {
2001
2002	    bytesPerBit *= 2;
2003	    if (bytesPerBit > kAppleRAIDMaxBitMapBytesPerBit) {
2004		bytesPerBit = kAppleRAIDMinBitMapBytesPerBit;
2005		bitMapSize += kAppleRAIDMinBitMapSize;
2006	    }
2007	    bitsNeeded = partitionSize / bytesPerBit;
2008	    bitsNeeded += partitionSize % bytesPerBit ? 1 : 0;
2009	}
2010    }
2011
2012    return bitMapSize;
2013}
2014
2015static AppleRAIDExtentOnDisk *
2016allocateExtent(AppleRAIDExtentOnDisk * firstExtent,  UInt64 lvgExtentCount, UInt64 size, CFStringRef location, UInt64 * extentCount)
2017{
2018
2019    // XXXTOC need to look at location
2020
2021    *extentCount = 0;
2022    AppleRAIDExtentOnDisk dummyExtent = {0, 0};
2023
2024    AppleRAIDExtentOnDisk * newExtents = malloc(sizeof(AppleRAIDExtentOnDisk));
2025    if (!newExtents) return NULL;
2026
2027    while (size) {
2028
2029	AppleRAIDExtentOnDisk * prevExtent = &dummyExtent;
2030	AppleRAIDExtentOnDisk * nextExtent = firstExtent;
2031	AppleRAIDExtentOnDisk * prevLargestExtent = 0;
2032	AppleRAIDExtentOnDisk * nextLargestExtent = 0;
2033	UInt64 gap = 0;
2034	UInt64 largestGap = 0;
2035
2036	// there should always be an ending extent for the metadata
2037	while (nextExtent < firstExtent + lvgExtentCount) {
2038
2039	    gap = nextExtent->extentByteOffset - (prevExtent->extentByteOffset + prevExtent->extentByteCount);
2040
2041	    // IOLog1("  existing extent at %lld, size %lld\n", prevExtent->extentByteOffset, prevExtent->extentByteCount);
2042
2043	    if (gap >= size) break;
2044
2045	    if (gap > largestGap) {
2046		largestGap = gap;
2047		prevLargestExtent = prevExtent;
2048		nextLargestExtent = nextExtent;
2049	    }
2050
2051	    prevExtent = nextExtent;
2052	    nextExtent++;
2053	}
2054
2055	if (largestGap && gap < size) {
2056	    prevExtent = prevLargestExtent;
2057	    nextExtent = nextLargestExtent;
2058	    gap = nextExtent->extentByteOffset - (prevExtent->extentByteOffset + prevExtent->extentByteCount);
2059	    IOLog1("largest extent found is %lld, wanted %lld\n", gap, size);
2060	}
2061
2062	if (!gap) {
2063	    free(newExtents);
2064	    return NULL;
2065	}
2066
2067	if (gap) {
2068
2069	    if (*extentCount) {
2070		newExtents = reallocf(newExtents, sizeof(AppleRAIDExtentOnDisk) * (*extentCount + 1));
2071		if (!newExtents) return NULL;
2072	    }
2073
2074	    newExtents[*extentCount].extentByteOffset = prevExtent->extentByteOffset + prevExtent->extentByteCount;
2075	    newExtents[*extentCount].extentByteCount = MIN(gap, size);
2076
2077	    IOLog1("Allocated new extent at %lld, size %lld\n", newExtents[*extentCount].extentByteOffset, newExtents[*extentCount].extentByteCount);
2078
2079	    prevExtent->extentByteCount += MIN(gap, size);  // this does not stick if it is the dummy extent (which is ok if we call this last)
2080
2081	    *extentCount += 1;
2082	    size -= MIN(gap, size);
2083	}
2084    }
2085
2086    if (!size) return newExtents;
2087
2088    free(newExtents);
2089    return NULL;
2090}
2091
2092
2093static UInt64 growLastExtent(CFMutableDataRef extentData, AppleRAIDExtentOnDisk * lvgExtentList, UInt64 lvgExtentCount, UInt64 newSize)
2094{
2095    CFIndex extentDataSize = CFDataGetLength(extentData);
2096    CFIndex index = 0;
2097    UInt64 volumeSize = 0;
2098    CFRange range;
2099    AppleRAIDExtentOnDisk foo, * extent = &foo;
2100
2101    // find volume's last extent & recalculate it's size
2102
2103    while (index < extentDataSize) {
2104
2105	range = CFRangeMake(index, sizeof(AppleRAIDExtentOnDisk));
2106	CFDataGetBytes(extentData, range, (void *)extent);
2107
2108	volumeSize += extent->extentByteCount;
2109
2110	index += sizeof(AppleRAIDExtentOnDisk);
2111    }
2112    UInt64 volumeEnd = extent->extentByteOffset + extent->extentByteCount;
2113
2114    UInt64 bytesNeeded = newSize - volumeSize;
2115
2116    // find a gap in the used lvg extents that starts at the volume's end
2117    // XXX this should use a binary search
2118
2119    AppleRAIDExtentOnDisk * lvgExtent;
2120    index = 0;
2121    UInt64 gapStart = 0;
2122    UInt64 gapSize;
2123    while (gapStart <= volumeEnd && index < (lvgExtentCount - 1)) {
2124
2125	lvgExtent = lvgExtentList + index;
2126
2127	gapStart = lvgExtent->extentByteOffset + lvgExtent->extentByteCount;
2128	gapSize = (lvgExtent + 1)->extentByteOffset - gapStart;
2129
2130	// found something!
2131	if (gapStart == volumeEnd && gapSize) {
2132
2133	    UInt64 bytesAvailable = MIN(bytesNeeded, gapSize);
2134	    extent->extentByteCount += bytesAvailable;
2135	    lvgExtent->extentByteCount += bytesAvailable;  // in case we reuse list later
2136	    CFDataReplaceBytes(extentData, range, (void *)extent, sizeof(AppleRAIDExtentOnDisk));
2137
2138	    return volumeSize + bytesAvailable;
2139	}
2140
2141	index++;
2142    }
2143
2144    return 0;
2145}
2146
2147
2148static UInt64 truncateExtents(CFMutableDataRef extentData, UInt64 newSize)
2149{
2150    CFIndex extentDataSize = CFDataGetLength(extentData);
2151    CFIndex index = 0;
2152    UInt64 extentEnd = 0;
2153    CFRange range;
2154    AppleRAIDExtentOnDisk foo, * extent = &foo;
2155
2156    while (index < extentDataSize) {
2157
2158	range = CFRangeMake(index, sizeof(AppleRAIDExtentOnDisk));
2159	CFDataGetBytes(extentData, range, (void *)extent);
2160
2161	extentEnd += extent->extentByteCount;
2162
2163	if (newSize <= extentEnd) {		// found it
2164
2165	    extent->extentByteCount -= extentEnd - newSize;
2166	    CFDataReplaceBytes(extentData, range, (void *)extent, sizeof(AppleRAIDExtentOnDisk));
2167	    CFDataSetLength(extentData, index + sizeof(AppleRAIDExtentOnDisk));
2168
2169	    return CFDataGetLength(extentData) / sizeof(AppleRAIDExtentOnDisk);  // the new extent count
2170	}
2171
2172	index += sizeof(AppleRAIDExtentOnDisk);
2173    }
2174
2175    // should never get here
2176    return 0;
2177}
2178
2179
2180UInt64 AppleRAIDGetUsableSize(UInt64 partitionSize, UInt64 chunkSize, UInt32 options)
2181{
2182    UInt64 usable = 0;
2183
2184    if (!chunkSize) {
2185	IOLog1("AppleRAIDGetUseableSize: zero chunkSize?\n");
2186	return 0;
2187    }
2188
2189    switch (options) {
2190
2191    case kAppleRAIDUsableSizeOptionNone:
2192	usable = ARHEADER_OFFSET(partitionSize) / chunkSize * chunkSize;
2193	break;
2194
2195    case kAppleRAIDUsableSizeOptionQuickRebuild:
2196	(void)calculateBitMapSize(partitionSize, chunkSize, &usable);
2197	break;
2198
2199    default:
2200	break;
2201    }
2202
2203    return usable;
2204}
2205
2206
2207CFDataRef
2208AppleRAIDDumpHeader(CFStringRef partitionName)
2209{
2210    memberInfo_t * memberInfo = getMemberInfo(partitionName);
2211    if (!memberInfo) return NULL;
2212
2213    CFDataRef data = readHeader(memberInfo);
2214
2215    freeMemberInfo(memberInfo);
2216
2217    return data;
2218}
2219
2220
2221// ***************************************************************************************************
2222//
2223// LVM interfaces
2224//
2225// ***************************************************************************************************
2226
2227CFMutableArrayRef
2228AppleLVMGetVolumesForGroup(AppleRAIDSetRef setRef, AppleRAIDMemberRef member)
2229{
2230    kern_return_t 	kr;
2231    size_t		propSize = kMaxIOConnectTransferSize;  // XXX buffer size?  use kAppleRAIDLVGVolumeCountKey
2232    CFMutableArrayRef	volumes = NULL;
2233    size_t		bufferSize = kAppleRAIDMaxUUIDStringSize * 2;
2234    char		buffer[bufferSize];
2235
2236    if (!CFStringGetCString(setRef, buffer, kAppleRAIDMaxUUIDStringSize, kCFStringEncodingUTF8)) {
2237	IOLog1("AppleLVMGetVolumesForGroup() CFStringGetCString failed on set ref?\n");
2238	return NULL;
2239    }
2240
2241    if (member) {
2242	if (!CFStringGetCString(member, &buffer[kAppleRAIDMaxUUIDStringSize], kAppleRAIDMaxUUIDStringSize, kCFStringEncodingUTF8)) {
2243	    IOLog1("AppleLVMGetVolumesForGroup() CFStringGetCString failed on member ref?\n");
2244	    return NULL;
2245	}
2246    } else {
2247	buffer[kAppleRAIDMaxUUIDStringSize] = 0;
2248    }
2249
2250    io_connect_t raidControllerPort = AppleRAIDOpenConnection();
2251    if (!raidControllerPort) return NULL;
2252
2253    char * propString = (char *)malloc(propSize);
2254
2255    kr = IOConnectCallStructMethod(raidControllerPort,		// an io_connect_t returned from IOServiceOpen().
2256				   kAppleLVMGetVolumesForGroup,	// an index to the function in the Kernel.
2257				   buffer,			// input
2258				   bufferSize,			// input size
2259				   propString,			// output
2260				   &propSize);			// output size (in/out)
2261
2262    if (kr == KERN_SUCCESS) {
2263        IOLog2("AppleLVMGetVolumesForGroup was successful.\n");
2264        IOLog2("size = %d, prop = %s\n", (int)propSize, (char *)propString);
2265
2266	volumes = (CFMutableArrayRef)IOCFUnserialize(propString, kCFAllocatorDefault, 0, NULL);
2267    } else {
2268        IOLog1("AppleLVMGetVolumesForGroup failed with 0x%x.\n", kr);
2269    }
2270
2271    free(propString);
2272
2273    AppleRAIDCloseConnection();
2274
2275    return volumes;
2276}
2277
2278
2279CFMutableDictionaryRef
2280AppleLVMGetVolumeProperties(AppleLVMVolumeRef volRef)
2281{
2282    kern_return_t 	kr;
2283    size_t		propSize = kMaxIOConnectTransferSize;
2284    CFMutableDictionaryRef props = NULL;
2285    size_t		bufferSize = kAppleRAIDMaxUUIDStringSize;
2286    char		buffer[bufferSize];
2287
2288    if (!CFStringGetCString(volRef, buffer, bufferSize, kCFStringEncodingUTF8)) {
2289	IOLog1("AppleLVMGetVolumeProperties() CFStringGetCString failed?\n");
2290	return NULL;
2291    }
2292
2293    io_connect_t raidControllerPort = AppleRAIDOpenConnection();
2294    if (!raidControllerPort) return NULL;
2295
2296    char * propString = (char *)malloc(propSize);
2297
2298    kr = IOConnectCallStructMethod(raidControllerPort,		// an io_connect_t returned from IOServiceOpen().
2299				   kAppleLVMGetVolumeProperties,// an index to the function in the Kernel.
2300				   buffer,			// input
2301				   bufferSize,			// input size
2302				   propString,			// output
2303				   &propSize);			// output size (in/out)
2304
2305    if (kr == KERN_SUCCESS) {
2306        IOLog2("AppleLVMGetVolumeProperties was successful.\n");
2307        IOLog2("size = %d, prop = %s\n", (int)propSize, (char *)propString);
2308
2309	props = (CFMutableDictionaryRef)IOCFUnserialize(propString, kCFAllocatorDefault, 0, NULL);
2310    }
2311
2312    free(propString);
2313
2314    AppleRAIDCloseConnection();
2315
2316    return props;
2317}
2318
2319static AppleRAIDExtentOnDisk *
2320getVolumeExtents(AppleLVMVolumeRef volRef, UInt64 * extentCount)
2321{
2322    kern_return_t 	kr;
2323    size_t		bufferSize = kAppleRAIDMaxUUIDStringSize;
2324    char		buffer[bufferSize];
2325    size_t		extentSize = kMaxIOConnectTransferSize;
2326    AppleRAIDExtentOnDisk * extents = NULL;
2327
2328    if (!extentCount || !*extentCount) return NULL;
2329
2330    if (!CFStringGetCString(volRef, buffer, bufferSize, kCFStringEncodingUTF8)) {
2331	IOLog1("AppleLVMGetVolumeExtents() CFStringGetCString failed?\n");
2332	return NULL;
2333    }
2334
2335    if (*extentCount * sizeof(AppleRAIDExtentOnDisk) > extentSize) return NULL;  // XXX buffer size
2336
2337    io_connect_t raidControllerPort = AppleRAIDOpenConnection();
2338    if (!raidControllerPort) return NULL;
2339
2340    AppleRAIDExtentOnDisk * extentsBuffer = (AppleRAIDExtentOnDisk *)malloc(extentSize);
2341
2342    kr = IOConnectCallStructMethod(raidControllerPort,		// an io_connect_t returned from IOServiceOpen().
2343				   kAppleLVMGetVolumeExtents,	// an index to the function in the Kernel.
2344				   buffer,			// input
2345				   bufferSize,			// input size
2346				   extentsBuffer,		// output
2347				   &extentSize);		// output size (in/out)
2348
2349    if (kr == KERN_SUCCESS) {
2350        IOLog2("AppleLVMGetVolumeExtents was successful.\n");
2351        IOLog2("size = %d, extent = %s\n", (int)extentSize, (char *)extentString);
2352
2353	extents = extentsBuffer;
2354	*extentCount = extentSize / sizeof(AppleRAIDExtentOnDisk);
2355    } else {
2356        IOLog2("AppleLVMGetVolumeExtents failed.\n");
2357	free(extentsBuffer);
2358    }
2359
2360    // XXX check for buffer too small error (first size is zero)
2361
2362    AppleRAIDCloseConnection();
2363
2364    return extents;
2365}
2366
2367
2368CFDataRef AppleLVMGetVolumeExtents(AppleLVMVolumeRef volRef)
2369{
2370    UInt64 extentCount = kMaxIOConnectTransferSize / sizeof(AppleRAIDExtentOnDisk);
2371
2372    AppleRAIDExtentOnDisk * extentList = getVolumeExtents(volRef, &extentCount);
2373    if (!extentList) return NULL;
2374
2375    if (extentList->extentByteCount == 0) {
2376	// retry with larger buffer
2377	extentCount = extentList->extentByteOffset;
2378	extentList = getVolumeExtents(volRef, &extentCount);
2379    }
2380    if (!extentList) return NULL;
2381
2382    CFDataRef extentData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)extentList,
2383						       extentCount * sizeof(AppleRAIDExtentOnDisk), kCFAllocatorMalloc);
2384    return extentData;
2385}
2386
2387static const char *lvDescriptionBuffer =
2388" <array> \n"
2389    "<dict> \n"
2390	"<key>" kAppleLVMVolumeTypeKey "</key>"		"<string>" kAppleLVMVolumeTypeConcat "</string> \n"
2391    "</dict> \n"
2392    "<dict> \n"
2393	"<key>" kAppleLVMVolumeTypeKey "</key>"		"<string>" kAppleLVMVolumeTypeSnapRO "</string> \n"
2394    "</dict> \n"
2395    "<dict> \n"
2396	"<key>" kAppleLVMVolumeTypeKey "</key>"		"<string>" kAppleLVMVolumeTypeSnapRW "</string> \n"
2397    "</dict> \n"
2398" </array> \n";
2399
2400CFMutableArrayRef AppleLVMGetVolumeDescription(void)
2401{
2402    CFStringRef errorString;
2403
2404    CFMutableArrayRef lvDescription = (CFMutableArrayRef)IOCFUnserialize(lvDescriptionBuffer, kCFAllocatorDefault, 0, &errorString);
2405    if (!lvDescription) {
2406	CFIndex	bufferSize = CFStringGetLength(errorString);
2407	bufferSize = CFStringGetMaximumSizeForEncoding(bufferSize, kCFStringEncodingUTF8) + 1;
2408	char *buffer = malloc(bufferSize);
2409	if (!buffer || !CFStringGetCString(errorString, buffer, bufferSize, kCFStringEncodingUTF8)) {
2410	    return NULL;
2411	}
2412
2413	IOLog1("AppleLVMGetVolumeDescription - failed while parsing raid definition file, error: %s\n", buffer);
2414	CFRelease(errorString);
2415	return NULL;
2416    }
2417
2418    return lvDescription;
2419}
2420
2421static const char *defaultCreateLVBuffer =
2422" <dict> \n"
2423    "<key>" kAppleLVMVolumeVersionKey "</key>"		"<integer size=\"32\">0x00030000</integer> \n"
2424    "<key>" kAppleLVMGroupUUIDKey "</key>"		"<string>internal error</string> \n"
2425    "<key>" kAppleLVMVolumeUUIDKey "</key>"		"<string>internal error</string> \n"
2426    "<key>" kAppleLVMVolumeSequenceKey "</key>"		"<integer size=\"32\">0</integer> \n"
2427    "<key>" kAppleLVMVolumeSizeKey "</key>"		"<integer size=\"64\">0x00000000</integer> \n"
2428    "<key>" kAppleLVMVolumeExtentCountKey "</key>"	"<integer size=\"64\">0x00000001</integer> \n"
2429    "<key>" kAppleLVMVolumeTypeKey "</key>"		"<string>internal error</string> \n"
2430    "<key>" kAppleLVMVolumeLocationKey "</key>"		"<string/> \n"
2431    "<key>" kAppleLVMVolumeContentHintKey "</key>"	"<string/> \n"
2432    "<key>" kAppleLVMVolumeNameKey "</key>"		"<string/> \n"
2433" </dict> \n";
2434
2435static CFMutableDictionaryRef
2436initLogicalVolumeProps(CFStringRef lvgUUIDString, CFStringRef volumeType, UInt64 size, CFStringRef location,
2437		       CFNumberRef sequenceNumber, CFDataRef extentData)
2438{
2439    CFStringRef errorString;
2440    UInt64 extentCount = CFDataGetLength(extentData) / sizeof(AppleRAIDExtentOnDisk);
2441    if (!extentCount) return NULL;
2442
2443    CFMutableDictionaryRef lvProps = (CFMutableDictionaryRef)IOCFUnserialize(defaultCreateLVBuffer, kCFAllocatorDefault, 0, &errorString);
2444    if (!lvProps) {
2445	CFIndex	bufferSize = CFStringGetLength(errorString);
2446	bufferSize = CFStringGetMaximumSizeForEncoding(bufferSize, kCFStringEncodingUTF8) + 1;
2447	char *buffer = malloc(bufferSize);
2448	if (!buffer || !CFStringGetCString(errorString, buffer, bufferSize, kCFStringEncodingUTF8)) {
2449	    return NULL;
2450	}
2451
2452	IOLog1("AppleLVMCreateVolume - failed while parsing logical volume template file, error: %s\n", buffer);
2453	CFRelease(errorString);
2454	return NULL;
2455    }
2456
2457    CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
2458    if (!uuid) return NULL;
2459    CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuid);
2460    CFRelease(uuid);
2461    if (!uuidString) return NULL;
2462
2463    CFNumberRef sizeProp = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &size);
2464    if (!sizeProp) return NULL;
2465
2466    CFNumberRef countProp = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &extentCount);
2467    if (!countProp) return NULL;
2468
2469    CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeUUIDKey), uuidString);
2470    CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMGroupUUIDKey), lvgUUIDString);
2471    CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeSequenceKey), sequenceNumber);
2472    CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeSizeKey), sizeProp);
2473    CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeLocationKey), location);
2474    CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeTypeKey), volumeType);
2475    CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeExtentCountKey), countProp);
2476
2477    CFDictionarySetValue(lvProps, CFSTR("_extent data_"), extentData);
2478
2479    CFRelease(uuidString);
2480    CFRelease(sizeProp);
2481    CFRelease(countProp);
2482
2483    return lvProps;
2484}
2485
2486
2487CFMutableDictionaryRef
2488AppleLVMCreateVolume(AppleRAIDSetRef setRef, CFStringRef volumeType, UInt64 volumeSize, CFStringRef volumeLocation)
2489{
2490    CFMutableDictionaryRef lvProps = 0;
2491
2492    if (!setRef || !volumeType || !volumeSize || !volumeLocation) return NULL;
2493
2494    setInfo_t * lvgInfo = getSetInfo(setRef);
2495    if (!lvgInfo) return NULL;
2496
2497    // read in the logical volume group's free space data
2498    UInt64 lvgExtentCount = 0;
2499    CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(lvgInfo->setProps, CFSTR(kAppleRAIDLVGExtentsKey));
2500    if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &lvgExtentCount);
2501
2502    UInt64 lvgFreeSpace = 0;
2503    number = (CFNumberRef)CFDictionaryGetValue(lvgInfo->setProps, CFSTR(kAppleRAIDLVGFreeSpaceKey));
2504    if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &lvgFreeSpace);
2505
2506    if (volumeSize > lvgFreeSpace) {
2507	printf("AppleRAID: Insufficent free space to create requested logical volume.\n");
2508	return NULL;
2509    }
2510
2511    // Ask for the extent list
2512    AppleRAIDExtentOnDisk * lvgExtentList = getVolumeExtents(setRef, &lvgExtentCount);
2513    if (!lvgExtentList) goto error;
2514
2515    // XXXTOC use kAppleRAIDMemberStartKey to find a member's startOffset
2516    // and then use that move the extentList pointer to start of that member
2517    // could also use the lvg extents to calculate the free space per member
2518    // to help spread out new volumes
2519
2520    UInt64 extentCount = 0;
2521    AppleRAIDExtentOnDisk * extentList = allocateExtent(lvgExtentList, lvgExtentCount, volumeSize, volumeLocation, &extentCount);
2522    if (!extentList) goto error;
2523
2524    CFDataRef extentData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)extentList,
2525						       extentCount * sizeof(AppleRAIDExtentOnDisk), kCFAllocatorMalloc);
2526    if (!extentData) goto error;
2527
2528    // set up disk block(s) for lv entry
2529    const void * sequenceProp = CFDictionaryGetValue(lvgInfo->setProps, CFSTR(kAppleRAIDSequenceNumberKey));
2530    if (!sequenceProp) goto error;
2531    CFStringRef lvgUUIDString = CFDictionaryGetValue(lvgInfo->setProps, CFSTR(kAppleRAIDSetUUIDKey));
2532    if (!lvgUUIDString) goto error;
2533
2534    lvProps = initLogicalVolumeProps(lvgUUIDString, volumeType, volumeSize, volumeLocation, sequenceProp, extentData);
2535    if (!lvProps) goto error;
2536
2537    freeSetInfo(lvgInfo);
2538
2539    return lvProps;
2540
2541error:
2542    // clean up
2543    if (lvProps) CFRelease(lvProps);
2544    freeSetInfo(lvgInfo);
2545
2546    return NULL;
2547}
2548
2549
2550bool
2551AppleLVMModifyVolume(CFMutableDictionaryRef lvProps, CFStringRef key, void * value)
2552{
2553    CFStringRef errorString;
2554
2555    CFMutableDictionaryRef defaultLVProps = (CFMutableDictionaryRef)IOCFUnserialize(defaultCreateLVBuffer, kCFAllocatorDefault, 0, &errorString);
2556    if (!defaultLVProps) {
2557	CFIndex	bufferSize = CFStringGetLength(errorString);
2558	bufferSize = CFStringGetMaximumSizeForEncoding(bufferSize, kCFStringEncodingUTF8) + 1;
2559	char *buffer = malloc(bufferSize);
2560	if (!buffer || !CFStringGetCString(errorString, buffer, bufferSize, kCFStringEncodingUTF8)) {
2561	    goto error;
2562	}
2563
2564	IOLog1("AppleLVMModifyVolume - failed while parsing logical volume template file, error: %s\n", buffer);
2565	CFRelease(errorString);
2566	goto error;
2567    }
2568
2569    const void * defaultValue = CFDictionaryGetValue(defaultLVProps, key);
2570    if (!defaultValue) goto error;
2571
2572    if (CFGetTypeID(defaultValue) != CFGetTypeID(value)) goto error;
2573
2574    AppleRAIDSetRef lvgRef = (CFStringRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMGroupUUIDKey));
2575    if (!lvgRef) return false;
2576    CFMutableDictionaryRef lvgProps = AppleRAIDGetSetProperties(lvgRef);
2577    if (!lvgProps) return false;
2578    const void * sequenceNumber = CFDictionaryGetValue(lvgProps, CFSTR(kAppleRAIDSequenceNumberKey));
2579    if (!sequenceNumber) return false;
2580    CFDictionarySetValue(lvProps, CFSTR(kAppleLVMVolumeSequenceKey), sequenceNumber);
2581    CFRelease(lvgProps);
2582
2583    CFDictionarySetValue(lvProps, key, value);
2584
2585    CFRelease(defaultLVProps);
2586
2587    return true;
2588
2589error:
2590    if (defaultLVProps) CFRelease(defaultLVProps);
2591    return false;
2592}
2593
2594static AppleLVMVolumeOnDisk *
2595buildLVMetaDataBlock(CFMutableDictionaryRef lvProps, CFDataRef extentData)
2596{
2597    CFDataRef propData = 0;
2598
2599    AppleRAIDExtentOnDisk * extentList = (AppleRAIDExtentOnDisk *)CFDataGetBytePtr(extentData);
2600    UInt64 extentCount = CFDataGetLength(extentData) / sizeof(AppleRAIDExtentOnDisk);
2601    if (!extentCount || !extentList) return NULL;
2602
2603    AppleLVMVolumeOnDisk * lvData = calloc(1, kAppleLVMVolumeOnDiskMinSize);
2604    if (!lvData) return NULL;
2605
2606    strlcpy(lvData->lvMagic, kAppleLVMVolumeMagic, sizeof(lvData->lvMagic));
2607    lvData->lvHeaderSize = kAppleLVMVolumeOnDiskMinSize;
2608    lvData->lvExtentsCount = extentCount;
2609
2610    // strip any internal keys from the dictionary before writing to disk
2611    CFMutableDictionaryRef cleanProps = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, lvProps);
2612    if (!cleanProps) goto error;
2613    CFIndex propCount = CFDictionaryGetCount(cleanProps);
2614    if (!propCount) goto error;
2615    const void ** keys = calloc(propCount, sizeof(void *));
2616    if (!keys) goto error;
2617    CFDictionaryGetKeysAndValues(cleanProps, keys, NULL);
2618    UInt32 i;
2619    for (i = 0; i < propCount; i++) {
2620	if (!CFStringHasPrefix(keys[i], CFSTR("AppleLVM-"))) {
2621	    CFDictionaryRemoveValue(cleanProps, keys[i]);
2622	}
2623    }
2624    CFDictionaryRemoveValue(cleanProps, CFSTR(kAppleLVMVolumeStatusKey));	// redundant
2625
2626    propData = IOCFSerialize(cleanProps, kNilOptions);
2627    if (!propData) {
2628	IOLog1("AppleRAIDLib - serialize on logical data props failed\n");
2629	goto error;
2630    }
2631    bcopy(CFDataGetBytePtr(propData), lvData->plist, CFDataGetLength(propData));
2632
2633    IOLog1("LogicalVolumeProps = %s\n", lvData->plist);
2634
2635    // start extents on multiple of sizeof(AppleRAIDExtentOnDisk) after the plist
2636    UInt32 firstExtent = CFDataGetLength(propData);
2637    firstExtent = firstExtent + (UInt32)((char *)lvData->plist - (char *)lvData);
2638    firstExtent = firstExtent + sizeof(AppleRAIDExtentOnDisk) - 1;
2639    firstExtent = firstExtent / sizeof(AppleRAIDExtentOnDisk) * sizeof(AppleRAIDExtentOnDisk);
2640
2641    lvData->lvExtentsStart = firstExtent;
2642    AppleRAIDExtentOnDisk * extent = (AppleRAIDExtentOnDisk *)((char *)lvData + firstExtent);
2643
2644    // sanity check before copy
2645    if (lvData->lvExtentsStart + sizeof(AppleRAIDExtentOnDisk) > lvData->lvHeaderSize) goto error;
2646
2647    for (i = 0; i < extentCount; i++) {
2648
2649	IOLog1("  %20llu - %12llu (%llu)\n",
2650	       extentList->extentByteOffset,
2651	       extentList->extentByteOffset + extentList->extentByteCount - 1,
2652	       extentList->extentByteCount);
2653
2654	*extent++ = *extentList++;
2655    }
2656
2657    CFRelease(propData);
2658    CFRelease(cleanProps);
2659
2660    return lvData;
2661
2662error:
2663    if (lvData) free(lvData);
2664    if (propData) CFRelease(propData);
2665    if (cleanProps) CFRelease(cleanProps);
2666    return NULL;
2667}
2668
2669
2670AppleLVMVolumeRef
2671AppleLVMUpdateVolume(CFMutableDictionaryRef volProps)
2672{
2673    CFStringRef volRef = (CFStringRef)CFDictionaryGetValue(volProps, CFSTR(kAppleLVMVolumeUUIDKey));
2674    if (!volRef) return NULL;
2675
2676    CFDataRef extentData = (CFDataRef)CFDictionaryGetValue(volProps, CFSTR("_extent data_"));
2677    if (extentData) {
2678	CFRetain(extentData);
2679	CFDictionaryRemoveValue(volProps, CFSTR("_extent data_"));
2680    } else {
2681	extentData = AppleLVMGetVolumeExtents(volRef);
2682	if (!extentData) return NULL;
2683    }
2684
2685    AppleLVMVolumeOnDisk * lvData = buildLVMetaDataBlock(volProps, extentData);
2686    if (!lvData) goto error;
2687
2688    io_connect_t raidControllerPort = AppleRAIDOpenConnection();
2689    if (!raidControllerPort) {
2690	IOLog1("AppleLVMUpdateVolume - failed connecting to raid controller object?\n");
2691	goto error;
2692    }
2693
2694    kern_return_t 	kr;
2695    char *		buffer = (char *)lvData;
2696    size_t		bufferSize = kAppleLVMVolumeOnDiskMinSize;
2697    char		updateData[0x1000];
2698    size_t		updateDataSize = sizeof(updateData);
2699
2700    kr = IOConnectCallStructMethod(raidControllerPort,		// an io_connect_t returned from IOServiceOpen().
2701				   kAppleLVMUpdateLogicalVolume,// an index to the function in the Kernel.
2702				   buffer,			// input
2703				   bufferSize,			// input size
2704				   updateData,			// output
2705				   &updateDataSize);		// output size (in/out)
2706
2707    AppleRAIDCloseConnection();
2708
2709    if (kr != KERN_SUCCESS) {
2710	IOLog1("AppleLVMUpdateVolume failed with %x calling client.\n", kr);
2711	goto error;
2712    }
2713
2714    CFRelease(extentData);
2715    free(lvData);
2716
2717    CFRetain(volRef);
2718    return volRef;
2719
2720error:
2721    if (extentData) CFRelease(extentData);
2722    if (lvData) free(lvData);
2723    return NULL;
2724}
2725
2726
2727bool
2728AppleLVMDestroyVolume(AppleLVMVolumeRef volRef)
2729{
2730    kern_return_t 	kr;
2731    size_t		bufferSize = kAppleRAIDMaxUUIDStringSize;
2732    char		buffer[bufferSize];
2733    char		returnData[0x1000];
2734    size_t		returnDataSize = sizeof(returnData);
2735
2736    if (!CFStringGetCString(volRef, buffer, bufferSize, kCFStringEncodingUTF8)) {
2737	IOLog1("AppleLVMDestroyVolume() CFStringGetCString failed?\n");
2738	return NULL;
2739    }
2740
2741    io_connect_t raidControllerPort = AppleRAIDOpenConnection();
2742    if (!raidControllerPort) return NULL;
2743
2744    kr = IOConnectCallStructMethod(raidControllerPort,		// an io_connect_t returned from IOServiceOpen().
2745				   kAppleLVMDestroyLogicalVolume,// an index to the function in the Kernel.
2746				   buffer,			// input
2747				   bufferSize,			// input size
2748				   returnData,			// output
2749				   &returnDataSize);		// output size (in/out)
2750
2751    if (kr != KERN_SUCCESS) {
2752	IOLog1("AppleLVMDestroyVolume failed with %x calling client.\n", kr);
2753    }
2754
2755    AppleRAIDCloseConnection();
2756
2757    return (kr == KERN_SUCCESS);
2758}
2759
2760// Logical Volume level manipulations
2761
2762UInt64
2763AppleLVMResizeVolume(CFMutableDictionaryRef lvProps, UInt64 newSize)
2764{
2765    UInt64 currentSize = 0;
2766    CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeSizeKey));
2767    if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &currentSize);
2768    if (!number || !currentSize) return 0;
2769    if (!newSize) return currentSize;
2770    if (currentSize == newSize) return 0;  // keeps us from calling update
2771
2772    CFStringRef volRef = (CFStringRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeUUIDKey));
2773    if (!volRef) return 0;
2774    AppleRAIDSetRef lvgRef = (CFStringRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMGroupUUIDKey));
2775    if (!lvgRef) return false;
2776    CFMutableDictionaryRef lvgProps = AppleRAIDGetSetProperties(lvgRef);
2777    if (!lvgProps) return false;
2778
2779    if (newSize > currentSize) {
2780	UInt64 freeSpace = 0;
2781	number = (CFNumberRef)CFDictionaryGetValue(lvgProps, CFSTR(kAppleRAIDLVGFreeSpaceKey));
2782	if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &freeSpace);
2783
2784	if (newSize > freeSpace) {
2785	    printf("AppleRAID: Insufficent free space to resize the logical volume.\n");
2786	    return 0;
2787	}
2788    }
2789
2790    CFDataRef originalExtentData;
2791    originalExtentData = (CFDataRef)CFDictionaryGetValue(lvProps, CFSTR("_extent data_"));
2792    if (!originalExtentData) {
2793	originalExtentData = AppleLVMGetVolumeExtents(volRef);
2794	if (!originalExtentData) return 0;
2795	CFDictionarySetValue(lvProps, CFSTR("_extent data_"), originalExtentData);
2796	CFRelease(originalExtentData);
2797    }
2798
2799    CFMutableDataRef extentData = CFDataCreateMutableCopy(kCFAllocatorDefault, 0, originalExtentData);
2800    CFDictionarySetValue(lvProps, CFSTR("_extent data_"), extentData);
2801    CFRelease(extentData);
2802
2803    const void * sequenceNumber = CFDictionaryGetValue(lvgProps, CFSTR(kAppleRAIDSequenceNumberKey));
2804    if (!sequenceNumber) return false;
2805    CFDictionarySetValue(lvProps, CFSTR(kAppleLVMVolumeSequenceKey), sequenceNumber);
2806    CFRelease(lvgProps);
2807
2808    //
2809    // truncate volume
2810    //
2811
2812    if (newSize < currentSize) {
2813
2814	UInt64 newExtentCount = truncateExtents(extentData, newSize);
2815	if (!newExtentCount) return 0;
2816
2817	// set the size and extent count properties
2818	number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &newSize);
2819	CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeSizeKey), number);
2820	number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &newExtentCount);
2821	CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeExtentCountKey), number);
2822
2823	return newSize;
2824    }
2825
2826    // fetch the lvg extent lists
2827
2828    AppleRAIDSetRef setRef = (CFStringRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMGroupUUIDKey));
2829    if (!setRef) return 0;
2830
2831    setInfo_t * lvgInfo = getSetInfo(setRef);
2832    if (!lvgInfo) return 0;
2833
2834    // read in the logical volume group's free space data
2835    UInt64 lvgExtentCount = 0;
2836    number = (CFNumberRef)CFDictionaryGetValue(lvgInfo->setProps, CFSTR(kAppleRAIDLVGExtentsKey));
2837    if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &lvgExtentCount);
2838
2839    // Ask for the extent list
2840    AppleRAIDExtentOnDisk * lvgExtentList = getVolumeExtents(setRef, &lvgExtentCount);
2841    if (!lvgExtentList) return 0;
2842
2843    // and peferred allocation region
2844    CFStringRef volumeLocation = (CFStringRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeLocationKey));
2845    if (!volumeLocation) return 0;
2846
2847    //
2848    // try to extend the current final extent
2849    //
2850
2851    UInt64 size = growLastExtent(extentData, lvgExtentList, lvgExtentCount, newSize);
2852    if (size) {
2853
2854	number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &newSize);
2855	CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeSizeKey), number);
2856
2857	if (size == newSize) return newSize;
2858
2859	// we got something, but not enough
2860	currentSize = size;
2861    }
2862
2863    //
2864    // try to allocate a new extent(s)
2865    //
2866
2867    UInt64 extentCount = 0;
2868    AppleRAIDExtentOnDisk * extentList = allocateExtent(lvgExtentList, lvgExtentCount, newSize - currentSize, volumeLocation, &extentCount);
2869    if (!extentList) return 0;
2870
2871    CFDataAppendBytes(extentData, (const UInt8 *)extentList, extentCount * sizeof(AppleRAIDExtentOnDisk));
2872    free(extentList);
2873
2874    number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &newSize);
2875    CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeSizeKey), number);
2876
2877    UInt64 newExtentCount = 0;
2878    number = (CFNumberRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeExtentCountKey));
2879    if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &newExtentCount);
2880    newExtentCount += extentCount;
2881    number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &newExtentCount);
2882    if (number) CFDictionaryReplaceValue(lvProps, CFSTR(kAppleLVMVolumeExtentCountKey), number);
2883    if (number) CFRelease(number);
2884
2885    return newSize;
2886}
2887
2888
2889CFMutableDictionaryRef
2890AppleLVMSnapShotVolume(CFMutableDictionaryRef lvProps, CFStringRef snapType, UInt64 snapSize)
2891{
2892    UInt64 lvSize = 0;
2893    CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeSizeKey));
2894    if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &lvSize);
2895    if (!number || !lvSize) return NULL;
2896
2897    snapSize = MIN(snapSize, lvSize);
2898
2899    UInt64 bitmapSize = calculateBitMapSize(lvSize, 0, NULL);
2900
2901    CFStringRef lvgUUID = (CFStringRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMGroupUUIDKey));
2902    if (!lvgUUID) return NULL;
2903    CFStringRef lvUUID = (CFStringRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeUUIDKey));
2904    if (!lvgUUID) return NULL;
2905    CFStringRef location = (CFStringRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeLocationKey));
2906    if (!lvgUUID) return NULL;
2907
2908    // this needs two logical volumes, one for data and one for bitmap/extent (how to resize?)
2909
2910    CFMutableDictionaryRef bitmap = AppleLVMCreateVolume(lvgUUID, CFSTR(kAppleLVMVolumeTypeBitMap), bitmapSize, CFSTR(kAppleLVMVolumeLocationFast));
2911    if (!bitmap) return NULL;
2912    CFDictionarySetValue(bitmap, CFSTR(kAppleLVMParentUUIDKey), lvUUID);
2913    CFStringRef bitmapUUID = AppleLVMUpdateVolume(bitmap);
2914    if (!bitmapUUID) return NULL;
2915
2916    CFMutableDictionaryRef snap = AppleLVMCreateVolume(lvgUUID, snapType, snapSize, location);
2917    if (!snap) return NULL;
2918    CFDictionarySetValue(snap, CFSTR(kAppleLVMParentUUIDKey), lvUUID);
2919    CFRelease(bitmap);
2920
2921    return snap;
2922}
2923
2924
2925bool
2926AppleLVMMigrateVolume(AppleLVMVolumeRef volRef, AppleRAIDMemberRef toRef, CFStringRef volumeLocation)
2927{
2928    return false;
2929}
2930
2931
2932// Logical Group Member level manipulations
2933
2934AppleLVMVolumeRef
2935AppleLVMRemoveMember(AppleLVMVolumeRef volRef, AppleRAIDMemberRef memberRef)
2936{
2937    return NULL;
2938}
2939
2940
2941AppleLVMVolumeRef
2942AppleLVMMergeGroups(AppleRAIDSetRef setRef, AppleRAIDSetRef donorSetRef)
2943{
2944    return NULL;
2945}
2946