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, ®istryEntry, 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, ®istryEntry, 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, ¤tSize); 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