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 "AppleRAID.h"
24
25#define super IOService
26OSDefineMetaClassAndStructors(AppleRAID, IOService);
27
28bool AppleRAID::init()
29{
30    if (super::init() == false) return false;
31
32    raidSets = OSDictionary::withCapacity(0x10);
33    raidMembers = OSDictionary::withCapacity(0x10);
34    logicalVolumes = OSDictionary::withCapacity(0x10);
35
36    return (raidSets && raidMembers && logicalVolumes);
37}
38
39void AppleRAID::free()
40{
41    if (raidSets) {
42        raidSets->release();
43        raidSets = 0;
44    }
45    if (raidMembers) {
46        raidMembers->release();
47        raidMembers = 0;
48    }
49    if (logicalVolumes) {
50        logicalVolumes->release();
51        logicalVolumes = 0;
52    }
53
54    super::free();
55}
56
57// **************************************************************************************************
58
59void AppleRAID::addSet(AppleRAIDSet *set)
60{
61    const OSString * uuid = set->getUUID();
62
63    if (uuid) {
64	raidSets->setObject(uuid, set);
65    }
66}
67
68void AppleRAID::removeSet(AppleRAIDSet * set)
69{
70    const OSString * uuid = set->getUUID();
71
72    if (uuid) {
73	raidSets->removeObject(uuid);
74    }
75}
76
77AppleRAIDSet * AppleRAID::findSet(const OSString *uuid)
78{
79    return OSDynamicCast(AppleRAIDSet, raidSets->getObject(uuid));
80}
81
82AppleRAIDSet * AppleRAID::findSet(AppleRAIDMember * member)
83{
84    const OSString * setUUID = member->getSetUUID();
85    if (setUUID == 0) return 0;
86
87    // top level set has both UUID's set the same
88
89    return OSDynamicCast(AppleRAIDSet, raidSets->getObject(setUUID));
90}
91
92// **************************************************************************************************
93
94void AppleRAID::addMember(AppleRAIDMember *member)
95{
96    const OSString * uuid = member->getUUID();
97
98    if (uuid) {
99	raidMembers->setObject(uuid, member);
100    }
101}
102
103void AppleRAID::removeMember(AppleRAIDMember * member)
104{
105    const OSString * uuid = member->getUUID();
106
107    if (uuid) {
108	raidMembers->removeObject(uuid);
109    }
110}
111
112AppleRAIDMember * AppleRAID::findMember(const OSString *uuid)
113{
114    return OSDynamicCast(AppleRAIDMember, raidMembers->getObject(uuid));
115}
116
117// **************************************************************************************************
118
119void AppleRAID::addLogicalVolume(AppleLVMVolume *volume)
120{
121    const OSString * uuid = volume->getVolumeUUID();
122
123    if (uuid) {
124	logicalVolumes->setObject(uuid, volume);
125    }
126}
127
128void AppleRAID::removeLogicalVolume(AppleLVMVolume * volume)
129{
130    const OSString * uuid = volume->getVolumeUUID();
131
132    if (uuid) {
133	logicalVolumes->removeObject(uuid);
134    }
135}
136
137AppleLVMVolume * AppleRAID::findLogicalVolume(const OSString *uuid)
138{
139    return OSDynamicCast(AppleLVMVolume, logicalVolumes->getObject(uuid));
140}
141
142// **************************************************************************************************
143
144// this should only fail in drastic cases
145
146IOReturn AppleRAID::newMember(IORegistryEntry * child)
147{
148    AppleRAIDMember * member = OSDynamicCast(AppleRAIDMember, child);
149
150    // this code is running under the global raid lock
151    gAppleRAIDGlobals.lock();
152
153    IOLog1("AppleRAID::newMember(%p) entered.\n", child);
154
155    while (1) {
156
157	if (member == 0) break;
158
159	// Look up the members's uuid
160	const OSString * memberUUID = member->getUUID();
161        if (memberUUID == 0) break;
162
163	// check if member already exists?
164	if (findMember(memberUUID)) {
165	    IOLog("AppleRAID::newMember detected duplicate member %s in set \"%s\" (%s).\n",
166		  member->getUUIDString(), member->getSetNameString(), member->getSetUUIDString());
167	    // XXX should break the set, this is bad
168	    break;
169	}
170
171	addMember(member);
172
173        // Look up the set's uuid
174	const OSString * setUUID = member->getSetUUID();
175        if (setUUID == 0) {
176	    IOLog("AppleRAID::newMember member %s in set \"%s\" has a corrupted header (no set UUID).\n",
177		  member->getUUIDString(), member->getSetNameString());
178	    break;
179	}
180
181	AppleRAIDSet * set = findSet(setUUID);
182
183	bool firstTime = set == 0;
184
185        // If the unique name was not found then create a new raid set
186        if (!set) {
187
188	    OSString * raidLevel = OSDynamicCast(OSString, member->getHeaderProperty(kAppleRAIDLevelNameKey));
189	    if (!raidLevel) {
190		IOLog("AppleRAID::newMember member %s in set \"%s\" (%s) has a corrupted header (no RAID level).\n",
191		  member->getUUIDString(), member->getSetNameString(), member->getSetUUIDString());
192		break;
193	    }
194
195	    IOLog1("AppleRAID::newMember(%p) new raid set, level = %s.\n", child, raidLevel->getCStringNoCopy());
196
197	    // XXX - should make this dynamic at run time, have raid levels register with the controller
198	    // XXX - or try to dynamically load plugins right here based on the raid level string
199
200	    if (raidLevel->isEqualTo(kAppleRAIDLevelNameConcat)) {
201		set = AppleRAIDConcatSet::createRAIDSet(member);
202	    } else
203	    if (raidLevel->isEqualTo(kAppleRAIDLevelNameMirror)) {
204		set = AppleRAIDMirrorSet::createRAIDSet(member);
205	    } else
206	    if (raidLevel->isEqualTo(kAppleRAIDLevelNameStripe)) {
207		set = AppleRAIDStripeSet::createRAIDSet(member);
208            } else
209	    if (raidLevel->isEqualTo(kAppleRAIDLevelNameLVG)) {
210		set = AppleLVMGroup::createRAIDSet(member);
211            }
212
213	    if (set) {
214		IOLog1("AppleRAID::newMember(%p) raid set \"%s\" (%s) successfully created.\n",
215		       child, set->getSetNameString(), set->getUUIDString());
216		addSet(set);
217		set->release();
218	    } else {
219		IOLog("AppleRAID::newMember unknown raid level %s.\n", raidLevel->getCStringNoCopy());
220	    }
221	}
222
223	// only punt on headerless raid partitions
224	if (!set) break;
225
226	// is this a live add of a new member?
227	// concat only, mirrors come in as spares
228	if (set->isPaused() && !set->isSetComplete()) {
229	    if (set->upgradeMember(member)) {
230		restartSet(set, true);
231		IOLog1("AppleRAID::newMember(%p) was successful (live add).\n", child);
232		gAppleRAIDGlobals.unlock();
233		return kIOReturnSuccess;
234	    }
235	    break;
236	}
237
238	// add member to raid set...
239	if (!set->addMember(member)) {
240	    if (!set->addSpare(member)) {
241		IOLog("AppleRAID::newMember was unable to add member %s to set \"%s\" (%s).\n",
242		      member->getUUIDString(), set->getSetNameString(), set->getUUIDString());
243		break;
244	    }
245	}
246
247	// try starting up the set
248	startSet(set);
249
250	// needed for user notifications
251	if (firstTime) set->registerService();
252
253	IOLog1("AppleRAID::newMember(%p) was successful.\n", child);
254	gAppleRAIDGlobals.unlock();
255	return kIOReturnSuccess;
256    }
257
258    IOLog1("AppleRAID::newMember(%p) failed.\n", child);
259
260    if (member) removeMember(member);
261
262    gAppleRAIDGlobals.unlock();
263    return kIOReturnUnformattedMedia;
264}
265
266IOReturn AppleRAID::oldMember(IORegistryEntry * child)
267{
268    IOLog1("AppleRAID::oldMember(%p) entered.\n", child);
269
270    // this code can not make any i/o requests, or it
271    // may deadlock this is because the driver calling
272    // this is holding it's lock
273
274    // this code is running under the global raid lock
275    gAppleRAIDGlobals.lock();
276
277    while (1) {
278
279	AppleRAIDMember * member = OSDynamicCast(AppleRAIDMember, child);
280	if (member == 0) break;
281
282	// still tracking this member?
283	const OSString * memberUUID = member->getUUID();
284	if (!memberUUID || findMember(memberUUID) != member) break;
285
286	// does it still belong to a raid set?
287	AppleRAIDSet * set = findSet(member);
288	if (!set) break;
289
290	set->retain();
291
292	removeMember(member);
293	set->removeMember(member, 0);
294
295	// if this member's set is empty then nuke the set as well
296 	if ((set->getActiveCount() == 0) && (set->getSpareCount() == 0)) {
297
298	    // if this set is part of another set then handle that first
299	    if (set->isRAIDMember()) {
300		oldMember(set);
301	    }
302
303	    IOLog1("AppleRAID::oldMember(%p) terminating parent raid set %p.\n", child, set);
304	    removeSet(set);
305	} else {
306	    set->release();
307	    set = NULL;
308	}
309
310	gAppleRAIDGlobals.unlock();
311
312	if (set) {
313	    IOLog("AppleRAID: terminating set \"%s\" (%s).\n", set->getSetNameString(), set->getUUIDString());
314	    set->terminate();
315	    set->release();
316	}
317
318	IOLog1("AppleRAID::oldMember(%p) was successful.\n", child);
319
320	return kIOReturnSuccess;
321    }
322
323    IOLog1("AppleRAID::oldMember(%p) lookup failed.\n", child);
324    gAppleRAIDGlobals.unlock();
325    return kIOReturnError;
326}
327
328
329void AppleRAID::recoverMember(IORegistryEntry * child)
330{
331    IOLog1("AppleRAID::recoverMember(%p) entered.\n", child);
332
333    AppleRAIDMember * member = OSDynamicCast(AppleRAIDMember, child);
334    if (member) {
335	AppleRAIDSet * set = findSet(member);
336	if (set) set->arSetCommandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, set, &AppleRAIDSet::recoverStart));
337    }
338}
339
340
341// *****************************************************************************
342
343void AppleRAID::startSet(AppleRAIDSet * set)
344{
345    IOLog1("AppleRAID::startSet(%p) entered.\n", set);
346
347    // this code is running under the global raid lock
348    assert(gAppleRAIDGlobals.islocked());
349
350    assert(!set->isPaused());
351
352    if (set->startSet()) {
353
354	IOLog1("AppleRAID::startSet: the set %p is started.\n", set);
355
356	// check for a "stacked" raid header, the first time
357	// we get here we don't know if the set is a raid member
358	if (!set->isRAIDMember()) set->start(NULL);
359    }
360
361    // let the evilness begin...
362    (void)set->publishSet();
363
364    return;
365}
366
367void AppleRAID::degradeSet(AppleRAIDSet *set)
368{
369    // this code is running under the global raid lock
370    gAppleRAIDGlobals.lock();
371
372    // make sure we still know about this set and it is in the right state
373    // the set has an extra retain on it to make this work (see ARM::init)
374    const OSString * setUUID = set->getUUID();
375    if ((set == findSet(setUUID)) && (set->getSetState() == kAppleRAIDSetStateInitializing)) {
376
377	assert(!set->isPaused());
378
379	IOLog("AppleRAID::degradeSet - starting the set \"%s\" (%s).\n",
380	      set->getSetNameString(), set->getUUIDString());
381
382	if (set->startSet()) {
383
384	    IOLog1("AppleRAID::degradeSet: the set %p is started.\n", set);
385
386	    // check for a "stacked" raid header
387	    if (!set->isRAIDMember()) set->start(NULL);
388	}
389
390	if (set->getSetState() == kAppleRAIDSetStateDegraded) {
391	    set->bumpSequenceNumber();
392	    set->writeRAIDHeader();
393	}
394
395	// let the evilness begin...
396	(void)set->publishSet();
397    }
398
399    gAppleRAIDGlobals.unlock();
400}
401
402void AppleRAID::restartSet(AppleRAIDSet *set, bool bump)
403{
404    IOLog1("AppleRAID::restartSet(%p) entered.\n", set);
405
406    // this code is running under the global raid lock
407    gAppleRAIDGlobals.lock();
408
409    IOLog("AppleRAID::restartSet - restarting set \"%s\" (%s).\n", set->getSetNameString(), set->getUUIDString());
410
411    (void)set->startSet();
412    if (bump) {
413	set->bumpSequenceNumber();
414	(void)set->writeRAIDHeader();
415    }
416    (void)set->publishSet();
417
418    gAppleRAIDGlobals.unlock();
419}
420
421
422IOReturn AppleRAID::updateSet(char * setInfoBuffer, uint32_t setInfoBufferSize, char * retBuffer, uint32_t * retBufferSize)
423{
424    IOReturn rc = kIOReturnSuccess;
425    IOLog1("AppleRAID::updateSet() entered\n");
426
427    if (!isOpen()) return kIOReturnNotOpen;
428    if (!setInfoBuffer || !setInfoBufferSize) return kIOReturnBadArgument;
429    if (!retBuffer || !retBufferSize) return kIOReturnBadArgument;
430
431    // this code is running under the global raid lock
432    gAppleRAIDGlobals.lock();
433
434    while (1) {
435	OSString * errmsg = 0;
436	OSDictionary * updateInfo = OSDynamicCast(OSDictionary, OSUnserializeXML(setInfoBuffer, &errmsg));
437	if (!updateInfo) {
438	    if (errmsg) {
439		IOLog("AppleRAID::updateSet - header parsing failed with %s\n", errmsg->getCStringNoCopy());
440		errmsg->release();
441	    }
442	    rc = kIOReturnBadArgument;
443	    break;
444	}
445
446	// find the set
447	const OSString * setUUIDString = OSDynamicCast(OSString, updateInfo->getObject(kAppleRAIDSetUUIDKey));
448	AppleRAIDSet * set = findSet(setUUIDString);
449	if (!set) { rc = kIOReturnBadArgument; break; };
450	updateInfo->removeObject(kAppleRAIDSetUUIDKey);
451
452	// if sequence number has changed then bail, something has changed
453	OSNumber * number = OSDynamicCast(OSNumber, updateInfo->getObject(kAppleRAIDSequenceNumberKey));
454	if (!number) { rc = kIOReturnBadArgument;  break; };
455	UInt32 seqNum = number->unsigned32BitValue();
456	if (seqNum && seqNum != set->getSequenceNumber()) { rc = kIOReturnBadMessageID; break; };
457	updateInfo->removeObject(kAppleRAIDSequenceNumberKey);
458
459	number = OSDynamicCast(OSNumber, updateInfo->getObject("_update command_"));
460	if (number) {
461	    UInt32 subcommand = number->unsigned32BitValue();
462
463	    IOLog1("AppleRAID::updateSet() executing subcommand %d\n", (int)subcommand);
464
465	    switch (subcommand) {
466
467	    case kAppleRAIDUpdateResetSet:
468
469		startSet(set);	// rescan raid headers (stacked sets)
470		break;
471
472	    case kAppleRAIDUpdateDestroySet:
473
474		if (set->unpublishSet()) {
475		    if (!set->destroySet()) {
476			rc = kIOReturnError;
477		    }
478		}
479		break;
480
481	    default:
482		IOLog("AppleRAID::updateSet() unknown subcommand %d\n", (int)subcommand);
483	    }
484	}
485	updateInfo->removeObject("_update command_");
486
487	// for each remaining prop that has changed call a specific set function or merge in
488	if (updateInfo->getCount()) {
489
490	    // we only need to go one level higher since we are not changing
491	    // the state of any of member sets at that level
492	    AppleRAIDSet * parentSet = 0;
493	    if (set->isRAIDMember()) {
494		parentSet = findSet((AppleRAIDMember *)set);
495		IOLog1("AppleRAID::updateSet() pausing parent set %p.\n", parentSet);
496		IOCommandGate::Action pauseSetMethod = OSMemberFunctionCast(IOCommandGate::Action, parentSet, &AppleRAIDSet::pauseSet);
497		if (parentSet) parentSet->arSetCommandGate->runAction(pauseSetMethod, (void *)false);
498	    }
499	    IOLog1("AppleRAID::updateSet() pausing set %p.\n", set);
500	    IOCommandGate::Action pauseSetMethod = OSMemberFunctionCast(IOCommandGate::Action, set, &AppleRAIDSet::pauseSet);
501	    set->arSetCommandGate->runAction(pauseSetMethod, (void *)false);
502
503	    if (!set->reconfigureSet(updateInfo)) rc = kIOReturnError;
504
505	    // reallocate the member arrays and republish the set's IOMedia
506	    restartSet(set, true);
507
508	    // if the set is waiting for a new added disk leave it paused
509	    IOLog1("AppleRAID::updateSet() unpausing set.\n");
510
511	    set->arSetCommandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, set, &AppleRAIDSet::unpauseSet));
512	    if (parentSet) parentSet->arSetCommandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, parentSet, &AppleRAIDSet::unpauseSet));
513	}
514
515	*(UInt32 *)retBuffer = set->getSequenceNumber();   // XXX this looks wrong for the destroy case?
516	*retBufferSize = sizeof(UInt32);
517
518	updateInfo->release();
519
520	IOLog1("AppleRAID::updateSet() was %ssuccessful.\n", rc ? "un" : "");
521	break;
522    }
523
524    gAppleRAIDGlobals.unlock();
525
526    return rc;
527}
528
529
530// *****************************************************************************
531
532
533IOReturn AppleRAID::getListOfSets(UInt32 inFlags, char * outList, uint32_t * outListSize)
534{
535    OSCollectionIterator * iter = 0;
536    OSArray * keys = 0;
537    OSSerialize * s = 0;
538    IOReturn rc = kIOReturnError;
539
540    IOLog1("AppleRAID::getListOfSets() entered\n");
541
542    if (!isOpen()) return kIOReturnNotOpen;
543    if (!inFlags || !outList || !outListSize) return kIOReturnBadArgument;
544
545    outList[0] = 0;
546
547    // this code is running under the global raid lock
548    gAppleRAIDGlobals.lock();
549
550    unsigned int keyCount = raidSets->getCount();
551
552    while (keyCount) {
553
554	keys = OSArray::withCapacity(keyCount);
555	if (!keys) break;
556
557	iter = OSCollectionIterator::withCollection(raidSets);
558	if (!iter) break;
559
560	while (const OSString * setName = OSDynamicCast(OSString, iter->getNextObject())) {
561
562	    AppleRAIDSet * set = findSet(setName);
563	    if (!set) continue;
564
565	    bool addToList = false;
566
567	    if (inFlags && kAppleRAIDState) {
568		UInt32 state = set->getSetState();
569		addToList = (((state < kAppleRAIDSetStateOnline)    && (inFlags & kAppleRAIDOfflineSets)) ||
570			     ((state == kAppleRAIDSetStateOnline)   && (inFlags & kAppleRAIDOnlineSets))  ||
571			     ((state == kAppleRAIDSetStateDegraded) && (inFlags & kAppleRAIDDegradedSets)));
572	    }
573
574	    if (inFlags && kAppleRAIDVisibility) {
575		bool visible = !set->isRAIDMember();
576		addToList = visible ? (inFlags & kAppleRAIDVisibleSets) : (inFlags & kAppleRAIDInternalSets);
577	    }
578
579	    if (addToList) (void)keys->setObject(setName);
580	}
581
582	s = OSSerialize::withCapacity(keys->getCount() * 128);
583        if (!s) break;
584
585        s->clearText();
586        if (!keys->serialize(s)) break;
587
588	if (*outListSize < s->getLength()) {
589	    IOLog("AppleRAID::getListOfSets() return buffer too small, need %d bytes, received %d.\n",
590		  (int)s->getLength(), (int)*outListSize);
591	    rc = kIOReturnNoSpace;
592	    break;
593	}
594	*outListSize = s->getLength();
595
596	bcopy(s->text(), outList, *outListSize);
597
598	rc = kIOReturnSuccess;
599	break;
600    }
601    if (iter) iter->release();
602    if (keys) keys->release();
603    if (s) s->release();
604    if (keyCount == 0) rc = kIOReturnNoDevice;
605
606    if (rc) *outListSize = 0;
607
608    gAppleRAIDGlobals.unlock();
609
610    return rc;
611}
612
613
614IOReturn AppleRAID::getSetProperties(char * setString, uint32_t setStringSize, char * outProp, uint32_t * outPropSize)
615{
616    IOReturn rc = kIOReturnError;
617    const OSString * setName = 0;
618    AppleRAIDSet * set = 0;
619    OSDictionary * props = 0;
620    OSSerialize * s = 0;
621
622    IOLog1("AppleRAID::getSetProperties(%s) entered\n", setString);
623
624    if (!isOpen()) return kIOReturnNotOpen;
625    if (!setString || !outProp || !outPropSize) return kIOReturnBadArgument;
626
627    outProp[0] = 0;
628
629    // this code is running under the global raid lock
630    gAppleRAIDGlobals.lock();
631
632    while (1) {
633
634	setName = OSString::withCString(setString);
635	if (!setName) break;
636
637	set = findSet(setName);
638	if (!set) break;
639
640	IOLog1("setName = %s (%p)\n", setString, set);
641
642	// get the prop list from the set
643	props = set->getSetProperties();
644	if (!props) break;
645
646	s = OSSerialize::withCapacity(512);
647	if (!s) break;
648
649	s->clearText();
650	if (!props->serialize(s)) break;
651
652	if (*outPropSize < s->getLength()) {
653	    IOLog("AppleRAID::getSetProperties() return buffer too small, need %d bytes, received %d.\n",
654		  (int)s->getLength(), (int)*outPropSize);
655	    rc = kIOReturnNoSpace;
656	    break;
657	}
658
659	*outPropSize = s->getLength();
660	bcopy(s->text(), outProp, *outPropSize);
661
662	rc = kIOReturnSuccess;
663	break;
664    }
665    if (setName) setName->release();
666    if (props) props->release();
667    if (s) s->release();
668
669    if (rc) *outPropSize = 0;
670
671    gAppleRAIDGlobals.unlock();
672
673    return rc;
674}
675
676
677IOReturn AppleRAID::getMemberProperties(char * memberString, uint32_t memberStringSize, char * outProp, uint32_t * outPropSize)
678{
679    IOReturn rc = kIOReturnError;
680    const OSString * memberName = 0;
681    AppleRAIDMember * member = 0;
682    OSDictionary * props = 0;
683    OSSerialize * s = 0;
684
685    IOLog2("AppleRAID::getMemberProperties(%s) entered\n", memberString);
686
687    if (!isOpen()) return kIOReturnNotOpen;
688    if (!memberString || !outProp || !outPropSize) return kIOReturnBadArgument;
689
690    outProp[0] = 0;
691
692    // this code is running under the global raid lock
693    gAppleRAIDGlobals.lock();
694
695    while (1) {
696
697	memberName = OSString::withCString(memberString);
698	if (!memberName) break;
699
700	member = findMember(memberName);
701	if (!member) break;
702
703	IOLog1("memberName = %s (%p)\n", memberString, member);
704
705	// get the prop list from the member
706	props = member->getMemberProperties();
707	if (!props) break;
708
709	s = OSSerialize::withCapacity(512);
710	if (!s) break;
711
712	s->clearText();
713	if (!props->serialize(s)) break;
714
715	if (*outPropSize < s->getLength()) {
716	    IOLog("AppleRAID::getMemberProperties() return buffer too small, need %d bytes, received %d.\n",
717		  (int)s->getLength(), (int)*outPropSize);
718	    rc = kIOReturnNoSpace;
719	    break;
720	}
721
722	*outPropSize = s->getLength();
723	bcopy(s->text(), outProp, *outPropSize);
724
725	rc = kIOReturnSuccess;
726	break;
727    }
728    if (memberName) memberName->release();
729    if (props) props->release();
730    if (s) s->release();
731
732    if (rc) *outPropSize = 0;
733
734    gAppleRAIDGlobals.unlock();
735
736    return rc;
737}
738
739// *****************************************************************************
740
741IOReturn AppleRAID::getVolumesForGroup(char * lvgString, uint32_t lvgStringSize, char * arrayString, uint32_t * outArraySize)
742{
743    IOReturn rc = kIOReturnError;
744    const OSString * lvgName = 0;
745    AppleLVMGroup * lvg = 0;
746    const OSString * memberName = 0;
747    AppleRAIDMember * member = 0;
748    OSArray * array = 0;
749    OSSerialize * s = 0;
750
751    IOLog1("AppleRAID::getVolumesForGroup(%s) for member %s entered\n",
752	   lvgString, lvgString[kAppleRAIDMaxUUIDStringSize] ? &lvgString[kAppleRAIDMaxUUIDStringSize] : "<all>");
753
754    if (!isOpen()) return kIOReturnNotOpen;
755    if (!lvgString || !arrayString || !outArraySize) return kIOReturnBadArgument;
756
757    arrayString[0] = 0;
758
759    // this code is running under the global raid lock
760    gAppleRAIDGlobals.lock();
761
762    while (1) {
763
764	lvgName = OSString::withCString(lvgString);
765	if (!lvgName) break;
766	lvg = OSDynamicCast(AppleLVMGroup, findSet(lvgName));
767	if (!lvg) break;
768
769	if (lvgString[kAppleRAIDMaxUUIDStringSize]) {
770	    memberName = OSString::withCString(&lvgString[kAppleRAIDMaxUUIDStringSize]);
771	    if (!memberName) break;
772	    member = findMember(memberName);
773	    if (!member) break;
774	}
775
776	// get the prop list from the volume
777	array = lvg->buildLogicalVolumeListFromTOC(member);
778	if (!array) break;
779
780	s = OSSerialize::withCapacity(512);
781	if (!s) break;
782
783	s->clearText();
784	if (!array->serialize(s)) break;
785
786	if (*outArraySize < s->getLength()) {
787	    IOLog("AppleRAID::getVolumeForGroup() return buffer too small, need %d bytes, received %d.\n",
788		  (int)s->getLength(), (int)*outArraySize);
789	    rc = kIOReturnNoSpace;
790	    break;
791	}
792
793	IOLog2("AppleRAID::getVolumesForGroup() size = %u array = %s\n", s->getLength(), s->text());
794
795	*outArraySize = s->getLength();
796	bcopy(s->text(), arrayString, *outArraySize);
797
798	rc = kIOReturnSuccess;
799	break;
800    }
801    if (lvgName) lvgName->release();
802    if (memberName) memberName->release();
803    if (array) array->release();
804    if (s) s->release();
805
806    if (rc) *outArraySize = 0;
807
808    gAppleRAIDGlobals.unlock();
809
810    return rc;
811}
812
813IOReturn AppleRAID::getVolumeProperties(char * lvString, uint32_t lvStringSize, char * outProp, uint32_t * outPropSize)
814{
815    IOReturn rc = kIOReturnError;
816    const OSString * lvName = 0;
817    AppleLVMVolume * lv = 0;
818    OSDictionary * props = 0;
819    OSSerialize * s = 0;
820
821    IOLog1("AppleRAID::getVolumeProperties(%s) entered\n", lvString);
822
823    if (!isOpen()) return kIOReturnNotOpen;
824    if (!lvString || !outProp || !outPropSize) return kIOReturnBadArgument;
825
826    outProp[0] = 0;
827
828    // this code is running under the global raid lock
829    gAppleRAIDGlobals.lock();
830
831    while (1) {
832
833	lvName = OSString::withCString(lvString);
834	if (!lvName) break;
835
836	lv = findLogicalVolume(lvName);
837	if (!lv) break;
838
839	// get the prop list from the volume
840	props = lv->getVolumeProperties();
841	if (!props) break;
842
843	s = OSSerialize::withCapacity(512);
844	if (!s) break;
845
846	s->clearText();
847	if (!props->serialize(s)) break;
848
849	if (*outPropSize < s->getLength()) {
850	    IOLog("AppleRAID::getVolumeProperties() return buffer too small, need %d bytes, received %d.\n",
851		  (int)s->getLength(), (int)*outPropSize);
852	    rc = kIOReturnNoSpace;
853	    break;
854	}
855
856	*outPropSize = s->getLength();
857	bcopy(s->text(), outProp, *outPropSize);
858
859	rc = kIOReturnSuccess;
860	break;
861    }
862    if (lvName) lvName->release();
863    if (props) props->release();
864    if (s) s->release();
865
866    if (rc) *outPropSize = 0;
867
868    gAppleRAIDGlobals.unlock();
869
870    return rc;
871}
872
873
874IOReturn AppleRAID::getVolumeExtents(char * lvString, uint32_t lvStringSize, char * extentsBuffer, uint32_t * extentsSize)
875{
876    IOReturn rc = kIOReturnError;
877    const OSString * lvName = 0;
878    AppleLVMVolume * lv = 0;
879    AppleLVMGroup * lvg = 0;
880
881    IOLog1("AppleRAID::getVolumeExtents(%s) entered\n", lvString);
882
883    if (!isOpen()) return kIOReturnNotOpen;
884    if (!lvString || !extentsBuffer || !extentsSize) return kIOReturnBadArgument;
885
886    // this code is running under the global raid lock
887    gAppleRAIDGlobals.lock();
888
889    while (1) {
890
891	lvName = OSString::withCString(lvString);
892	if (!lvName) break;
893
894	// get the extent list from the volume
895	AppleRAIDExtentOnDisk * extent = (AppleRAIDExtentOnDisk *)extentsBuffer;
896
897	lv = findLogicalVolume(lvName);
898	if (!lv) {
899
900	    // the lvg list is special
901	    lvg = OSDynamicCast(AppleLVMGroup, findSet(lvName));
902	    if (!lvg) break;
903
904	    UInt64 extentCount = lvg->getExtentCount();
905	    if (extentCount * sizeof(AppleRAIDExtentOnDisk) > *extentsSize) {
906		extent->extentByteOffset = extentCount;
907		extent->extentByteCount = 0;
908		*extentsSize = sizeof(AppleRAIDExtentOnDisk);
909		rc = kIOReturnSuccess;  // error is indicated via return buffer
910	    } else {
911		if (lvg->buildExtentList(extent)) {
912		    *extentsSize = extentCount * sizeof(AppleRAIDExtentOnDisk);
913		    rc = kIOReturnSuccess;
914		}
915	    }
916	    IOLog1("LVG %s (%p) has %llu total extents.\n", lvString, lvg, extentCount);
917	    break;
918	}
919
920	UInt64 extentCount = lv->getExtentCount();
921	if (extentCount * sizeof(AppleRAIDExtentOnDisk) > *extentsSize) {
922	    extent->extentByteOffset = extentCount;
923	    extent->extentByteCount = 0;
924	    *extentsSize = sizeof(AppleRAIDExtentOnDisk);
925	    rc = kIOReturnSuccess;  // error is indicated via return buffer
926	} else {
927	    if (lv->buildExtentList(extent)) {
928		*extentsSize = extentCount * sizeof(AppleRAIDExtentOnDisk);
929		rc = kIOReturnSuccess;
930	    }
931	}
932	IOLog1("LV %s (%p) has %llu extents.\n", lvString, lv, extentCount);
933
934	break;
935    }
936    if (lvName) lvName->release();
937
938    if (rc) *extentsSize = 0;
939
940    gAppleRAIDGlobals.unlock();
941
942    return rc;
943}
944
945
946IOReturn AppleRAID::updateLogicalVolume(char * lveBuffer, uint32_t lveBufferSize, char * retBuffer, uint32_t * retBufferSize)
947{
948    OSDictionary * lvProps = NULL;
949    IOReturn rc = kIOReturnBadArgument;
950    IOLog1("AppleRAID::updateLogicalVolume() entered\n");
951
952    if (!isOpen()) return kIOReturnNotOpen;
953    if (!lveBuffer || !lveBufferSize) return kIOReturnBadArgument;
954    if (!retBuffer || !retBufferSize) return kIOReturnBadArgument;
955
956    AppleLVMVolumeOnDisk * lve = (AppleLVMVolumeOnDisk *)lveBuffer;
957
958    // this code is running under the global raid lock
959    gAppleRAIDGlobals.lock();
960
961    while (1) {
962
963	lvProps = AppleLVMVolume::propsFromHeader(lve);
964	if (!lvProps) break;
965
966	// find the set
967	const OSString * setUUIDString = OSDynamicCast(OSString, lvProps->getObject(kAppleLVMGroupUUIDKey));
968	AppleLVMGroup * lvg = OSDynamicCast(AppleLVMGroup, findSet(setUUIDString));
969	if (!lvg) break;
970
971	// if sequence number has changed then bail, something has changed
972	// the logical volume should be updated with the current LVG sequence number
973	OSNumber * number = OSDynamicCast(OSNumber, lvProps->getObject(kAppleLVMVolumeSequenceKey));
974	if (!number) break;
975	UInt32 seqNum = number->unsigned32BitValue();
976	if (seqNum && seqNum != lvg->getSequenceNumber()) { rc = kIOReturnBadMessageID; break; };
977
978	// look up the volume UUID, this might be an update
979	const OSString * lvUUIDString = OSDynamicCast(OSString, lvProps->getObject(kAppleLVMVolumeUUIDKey));
980	AppleLVMVolume * lv = OSDynamicCast(AppleLVMVolume, findLogicalVolume(lvUUIDString));
981
982	// do something
983
984	if (lv) {
985	    rc = lvg->updateLogicalVolume(lv, lvProps, lve);
986	} else {
987	    rc = lvg->createLogicalVolume(lvProps, lve);
988	}
989
990	*(UInt32 *)retBuffer = lvg->getSequenceNumber();
991	*retBufferSize = sizeof(UInt32);
992
993	break;
994    }
995
996    gAppleRAIDGlobals.unlock();
997
998    if (lvProps) lvProps->release();
999
1000    IOLog1("AppleRAID::updateLogicalVolume() was %ssuccessful.\n", rc ? "un" : "");
1001    return rc;
1002}
1003
1004
1005IOReturn AppleRAID::destroyLogicalVolume(char * lvString, uint32_t lvStringSize, char * retBuffer, uint32_t * retBufferSize)
1006{
1007    IOReturn rc = kIOReturnBadArgument;
1008    IOLog1("AppleRAID::destroyLogicalVolume() entered\n");
1009
1010    if (!isOpen()) return kIOReturnNotOpen;
1011    if (!lvString || !lvStringSize) return kIOReturnBadArgument;
1012    if (!retBuffer || !retBufferSize) return kIOReturnBadArgument;
1013
1014    // this code is running under the global raid lock
1015    gAppleRAIDGlobals.lock();
1016
1017    while (1) {
1018	OSString * lvName = OSString::withCString(lvString);
1019	if (!lvName) break;
1020
1021	AppleLVMVolume * lv = findLogicalVolume(lvName);
1022	if (!lv) break;
1023
1024	const OSString * lvgUUID = lv->getGroupUUID();
1025	if (!lvgUUID) break;
1026	AppleLVMGroup * lvg = (AppleLVMGroup *)findSet(lvgUUID);
1027	if (!lvg) break;
1028
1029	// do something
1030	rc = lvg->destroyLogicalVolume(lv);
1031
1032	*(UInt32 *)retBuffer = lvg->getSequenceNumber();
1033	*retBufferSize = sizeof(UInt32);
1034
1035	break;
1036    }
1037
1038    gAppleRAIDGlobals.unlock();
1039
1040    IOLog1("AppleRAID::destroyLogicalVolume() was %ssuccessful.\n", rc ? "un" : "");
1041    return rc;
1042}
1043