1/* 2 * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* 24 * BLCreateBooterInformationDictionary.c 25 * bless 26 * 27 * Created by Shantonu Sen on 5/22/06. 28 * Copyright 2006-2007 Apple Inc. All Rights Reserved. 29 * 30 */ 31 32#include <stdlib.h> 33#include <unistd.h> 34#include <sys/stat.h> 35#include <sys/param.h> 36#include <sys/mount.h> 37 38#include <mach/mach_error.h> 39 40#include <IOKit/IOKitLib.h> 41#include <IOKit/IOBSD.h> 42 43#include <CoreFoundation/CoreFoundation.h> 44 45#include "bless.h" 46#include "bless_private.h" 47 48 49#include <IOKit/storage/IOMedia.h> 50#include <IOKit/storage/IOPartitionScheme.h> 51#if SUPPORT_APPLE_PARTITION_MAP 52#include <IOKit/storage/IOApplePartitionScheme.h> 53#endif 54#include <IOKit/storage/IOGUIDPartitionScheme.h> 55#include <IOKit/storage/IOStorageProtocolCharacteristics.h> 56 57static int addRAIDInfo(BLContextPtr context, CFDictionaryRef dict, 58 CFMutableArrayRef dataPartitions, 59 CFMutableArrayRef booterPartitions, 60 CFMutableArrayRef systemPartitions); 61 62 63static int addDataPartitionInfo(BLContextPtr context, io_service_t dataPartition, 64 CFMutableArrayRef dataPartitions, 65 CFMutableArrayRef booterPartitions, 66 CFMutableArrayRef systemPartitions); 67 68static int addPreferredSystemPartitionInfo(BLContextPtr context, 69 CFMutableArrayRef systemPartitions, 70 bool foundPreferred); 71 72 73bool isPreferredSystemPartition(BLContextPtr context, CFStringRef bsdName); 74 75/* 76 * For the given device, we return the set of Auxiliary Partitions and 77 * System Partitions. System Partitions are not OS-specific. This routine 78 * works for both APM and GPT disks, as well as IOMedia filters/aggregates 79 * Using those partition map types. 80 * 81 * A given partition may not have an Auxiliary Partitions nor System Partitions, 82 * for example a single HFS+ partition on an APM disk, or it may have many 83 * APs and SPs, for example a 4-member RAID mirror on GPT disks. 84 */ 85 86int BLCreateBooterInformationDictionary(BLContextPtr context, const char * bsdName, 87 CFDictionaryRef *outDict) 88{ 89 CFMutableArrayRef dataPartitions = NULL; 90 CFMutableArrayRef booterPartitions = NULL; 91 CFMutableArrayRef systemPartitions = NULL; 92 CFMutableDictionaryRef booters = NULL; 93 94 CFArrayRef array; 95 96 CFTypeRef bootData = NULL; 97 98 io_service_t rootDev; 99 int ret = 0; 100 bool gotOne; 101 102 dataPartitions = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 103 if(dataPartitions == NULL) 104 return 1; 105 106 booterPartitions = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 107 if(booterPartitions == NULL) 108 return 1; 109 110 systemPartitions = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 111 if(systemPartitions == NULL) 112 return 1; 113 114 booters = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 115 &kCFTypeDictionaryKeyCallBacks, 116 &kCFTypeDictionaryValueCallBacks); 117 if(booters == NULL) 118 return 1; 119 120 121 rootDev = IOServiceGetMatchingService(kIOMasterPortDefault, 122 IOBSDNameMatching(kIOMasterPortDefault, 0, bsdName)); 123 124 if(rootDev == IO_OBJECT_NULL) { 125 CFRelease(booters); 126 127 contextprintf(context, kBLLogLevelError, 128 "Could not get IOService for %s\n", bsdName); 129 return 2; 130 } 131 132 if(!IOObjectConformsTo(rootDev,kIOMediaClass)) { 133 CFRelease(booters); 134 CFRelease(dataPartitions); 135 CFRelease(booterPartitions); 136 CFRelease(systemPartitions); 137 138 IOObjectRelease(rootDev); 139 140 contextprintf(context, kBLLogLevelError, 141 "%s is not an IOMedia object\n", bsdName); 142 return 2; 143 } 144 145 bootData = IORegistryEntrySearchCFProperty( rootDev, 146 kIOServicePlane, 147 CFSTR(kIOBootDeviceKey), 148 kCFAllocatorDefault, 149 kIORegistryIterateRecursively| 150 kIORegistryIterateParents); 151 152 if(bootData) { 153 154 // if there's boot data, this is an IOMedia filter/aggregate publishing 155 // its data members 156 if(CFGetTypeID(bootData) == CFArrayGetTypeID()) { 157 CFIndex i, count = CFArrayGetCount(bootData); 158 159 for(i=0; i < count; i++) { 160 CFDictionaryRef dict = CFArrayGetValueAtIndex((CFArrayRef)bootData,i); 161 162 ret = addRAIDInfo(context, dict, 163 dataPartitions, 164 booterPartitions, 165 systemPartitions); 166 if(ret) { 167 break; 168 } 169 170 } 171 } else if( CFGetTypeID(bootData) == CFDictionaryGetTypeID()) { 172 173 ret = addRAIDInfo(context, (CFDictionaryRef)bootData, 174 dataPartitions, 175 booterPartitions, 176 systemPartitions); 177 } else { 178 contextprintf(context, kBLLogLevelError, 179 "Invalid boot data for %s\n", bsdName); 180 181 ret = 5;; 182 } 183 184 if(ret) { 185 CFRelease(bootData); 186 CFRelease(booters); 187 CFRelease(dataPartitions); 188 CFRelease(booterPartitions); 189 CFRelease(systemPartitions); 190 191 IOObjectRelease(rootDev); 192 193 return ret; 194 195 } 196 197 CFRelease(bootData); 198 199 } else { 200 ret = addDataPartitionInfo(context, rootDev, 201 dataPartitions, 202 booterPartitions, 203 systemPartitions); 204 if(ret) { 205 CFRelease(booters); 206 CFRelease(dataPartitions); 207 CFRelease(booterPartitions); 208 CFRelease(systemPartitions); 209 210 IOObjectRelease(rootDev); 211 212 return ret; 213 } 214 } 215 216 217 IOObjectRelease(rootDev); 218 219 goto gotinfo; 220gotinfo: 221 222 if(getenv("BL_PRIMARY_BOOTER_INDEX")) { 223 char *bindex = getenv("BL_PRIMARY_BOOTER_INDEX"); 224 CFIndex index = atoi(bindex); 225 226 if(index >= 0 && index < CFArrayGetCount(booterPartitions)) { 227 CFArrayExchangeValuesAtIndices(booterPartitions, 0, index); 228 } 229 } 230 231 // we have a set of systemPartitions. Reorder a preferred one to the front. 232 // if not, look for a preferred partition manually 233 // In the past, we just wanted to find one preferred partition, so 234 // addPreferredSystemPartitionInfo() would find one and put it at the front. 235 // But there is now a desire by some callers to be able to see all 236 // preferred ESPs. So our logic becomes a little convoluted: 237 // Previously, we would look at any partitions on devices associated 238 // with the passed-in mountpoint. If one of them was preferred, then 239 // make sure that one was first. If not, then (and only then) call 240 // addPreferred..., which would (hopefully) find at most one preferred 241 // partition. 242 // With the new requirements we need to put them all in, which means we 243 // have to unconditionally call addPreferred.... But we don't want to 244 // have a behavior change for those callers who only look at the first element 245 // of the array; i.e. we still want the first one to be associated with the 246 // passed-in mountpoint if there's a preferred one there. 247 // So with that in mind, we'll continue to start out the same. That is, 248 // look for a preferred ESP associated with the passed-in mountpoint and 249 // make sure it's first in the array. Then, call addPreferred unconditionally. 250 // But addPreferred will have two changes: 1) Don't stop after finding the first 251 // one, so we get all of them, and 2) Only put a found preferred ESP at the front 252 // if there isn't already one there. 253 254 gotOne = false; 255 if (CFArrayGetCount(systemPartitions) > 0) { 256 CFIndex i, count; 257 count = CFArrayGetCount(systemPartitions); 258 for (i=0; i < count; i++) { 259 CFStringRef testSP = CFArrayGetValueAtIndex(systemPartitions, i); 260 if (isPreferredSystemPartition(context, testSP)) { 261 if (i > 0) CFArrayExchangeValuesAtIndices(systemPartitions, 0, i); 262 gotOne = true; 263 break; 264 } 265 } 266 } 267 268 addPreferredSystemPartitionInfo(context, systemPartitions, gotOne); 269 270 array = CFArrayCreateCopy(kCFAllocatorDefault, dataPartitions); 271 CFDictionaryAddValue(booters, kBLDataPartitionsKey, array); 272 CFRelease(array); 273 CFRelease(dataPartitions); 274 275 array = CFArrayCreateCopy(kCFAllocatorDefault, booterPartitions); 276 CFDictionaryAddValue(booters, kBLAuxiliaryPartitionsKey, array); 277 CFRelease(array); 278 CFRelease(booterPartitions); 279 280 array = CFArrayCreateCopy(kCFAllocatorDefault, systemPartitions); 281 CFDictionaryAddValue(booters, kBLSystemPartitionsKey, array); 282 CFRelease(array); 283 CFRelease(systemPartitions); 284 285 contextprintf(context, kBLLogLevelVerbose, "Returning booter information dictionary:\n%s\n", 286 BLGetCStringDescription(booters)); 287 288 *outDict = booters; 289 290 return 0; 291} 292 293static int addRAIDInfo(BLContextPtr context, CFDictionaryRef dict, 294 CFMutableArrayRef dataPartitions, 295 CFMutableArrayRef booterPartitions, 296 CFMutableArrayRef systemPartitions) 297{ 298 CFStringRef bootpath = NULL; 299 io_string_t iostring; 300 io_service_t service; 301 302 int ret; 303 304 bootpath = CFDictionaryGetValue(dict, CFSTR(kIOBootDevicePathKey)); 305 if(bootpath == NULL || CFGetTypeID(bootpath) != CFStringGetTypeID()) { 306 contextprintf(context, kBLLogLevelError, "Could not find boot path entry\n"); 307 return 1; 308 } 309 310 if(!CFStringGetCString(bootpath,iostring,sizeof(iostring),kCFStringEncodingUTF8)) { 311 contextprintf(context, kBLLogLevelError, "Invalid UTF8 for path entry\n"); 312 return 2; 313 } 314 315 contextprintf(context, kBLLogLevelVerbose, "Aggregate boot path is %s\n" , iostring); 316 317 service = IORegistryEntryFromPath(kIOMasterPortDefault, iostring ); 318 if(service == IO_OBJECT_NULL) { 319 contextprintf(context, kBLLogLevelError, "Could not find IOKit entry for %s\n" , iostring); 320 return 4; 321 } 322 323 ret = addDataPartitionInfo(context, service, 324 dataPartitions, 325 booterPartitions, 326 systemPartitions); 327 328 IOObjectRelease(service); 329 330 return ret; 331} 332 333static int addDataPartitionInfo(BLContextPtr context, io_service_t dataPartition, 334 CFMutableArrayRef dataPartitions, 335 CFMutableArrayRef booterPartitions, 336 CFMutableArrayRef systemPartitions) 337{ 338 CFStringRef bsdName; 339 kern_return_t kret; 340 io_service_t parent; 341 uint32_t partitionID, searchID; 342 CFNumberRef partitionNum; 343 CFStringRef content; 344 bool needsBooter = false; 345 CFNumberRef neededBooterPartitionNum = NULL; 346 CFStringRef neededBooterContent = NULL; 347 CFStringRef neededSystemContent = NULL; 348 349 io_iterator_t childIterator; 350 io_service_t child; 351 352 /* don't require this at this point 353 kret = IORegistryEntryGetPath(dataPartition, kIODeviceTreePlane, devPath); 354 if(kret != KERN_SUCCESS) { 355 contextprintf(context, kBLLogLevelError, "Could not get path in device plane for service\n" ); 356 return 1; 357 } 358 */ 359 360 bsdName = IORegistryEntryCreateCFProperty(dataPartition, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0); 361 if(bsdName == NULL || (CFGetTypeID(bsdName) != CFStringGetTypeID())) { 362 if(bsdName) CFRelease(bsdName); 363 364 return 1; 365 } 366 367 partitionNum = IORegistryEntryCreateCFProperty(dataPartition, CFSTR(kIOMediaPartitionIDKey), kCFAllocatorDefault, 0); 368 if(partitionNum == NULL || (CFGetTypeID(partitionNum) != CFNumberGetTypeID())) { 369 if(partitionNum) CFRelease(partitionNum); 370 CFRelease(bsdName); 371 372 return 1; 373 } 374 375 content = (CFStringRef)IORegistryEntryCreateCFProperty(dataPartition, CFSTR(kIOMediaContentKey), kCFAllocatorDefault, 0); 376 if(content == NULL || (CFGetTypeID(content) != CFStringGetTypeID())) { 377 if(content) CFRelease(content); 378 CFRelease(partitionNum); 379 CFRelease(bsdName); 380 381 contextprintf(context, kBLLogLevelError, "Partition does not have Content key\n" ); 382 383 return 1; 384 } 385 386 387 if(!CFNumberGetValue(partitionNum,kCFNumberSInt32Type, &partitionID)) { 388 CFRelease(content); 389 CFRelease(partitionNum); 390 CFRelease(bsdName); 391 392 contextprintf(context, kBLLogLevelError, "Could not get Partition ID for service\n" ); 393 return 1; 394 } 395 396 if(!CFArrayContainsValue(dataPartitions,CFRangeMake(0, CFArrayGetCount(dataPartitions)), bsdName)) { 397 CFArrayAppendValue(dataPartitions, bsdName); 398 } 399 CFRelease(bsdName); 400 CFRelease(partitionNum); 401 402 kret = IORegistryEntryGetParentEntry(dataPartition, kIOServicePlane, &parent); 403 if(kret != KERN_SUCCESS) { 404 CFRelease(content); 405 406 contextprintf(context, kBLLogLevelError, "Could not get parent path in device plane for service\n" ); 407 return 1; 408 } 409 410#if SUPPORT_APPLE_PARTITION_MAP 411 if(IOObjectConformsTo(parent, kIOApplePartitionSchemeClass)) { 412 contextprintf(context, kBLLogLevelVerbose, "APM detected\n" ); 413 // from the OS point of view, only it's an HFS or boot partition, it needs a booter 414 415 if(CFEqual(content, CFSTR("Apple_HFS")) || 416 CFEqual(content, CFSTR("Apple_HFSX")) || 417 CFEqual(content, CFSTR("Apple_Boot")) || 418 CFEqual(content, CFSTR("Apple_Boot_RAID")) ) { 419 needsBooter = false; 420 } else { 421 needsBooter = true; 422 searchID = partitionID - 1; 423 neededBooterContent = CFSTR("Apple_Boot"); 424 neededBooterPartitionNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &searchID); 425 } 426 427 } else 428#endif // SUPPORT_APPLE_PARTITION_MAP 429 if(IOObjectConformsTo(parent, kIOGUIDPartitionSchemeClass)) { 430 contextprintf(context, kBLLogLevelVerbose, "GPT detected\n" ); 431 432 // from the OS point of view, only it's an HFS or boot partition, it needs a booter 433 if(CFEqual(content, CFSTR("48465300-0000-11AA-AA11-00306543ECAC")) || 434 CFEqual(content, CFSTR("426F6F74-0000-11AA-AA11-00306543ECAC"))) { 435 needsBooter = false; 436 } else { 437 needsBooter = true; 438 searchID = partitionID + 1; 439 neededBooterContent = CFSTR("426F6F74-0000-11AA-AA11-00306543ECAC"); 440 neededBooterPartitionNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &searchID); 441 } 442 443 neededSystemContent = CFSTR("C12A7328-F81F-11D2-BA4B-00A0C93EC93B"); 444 445 } else { 446 contextprintf(context, kBLLogLevelVerbose, "Other partition scheme detected\n" ); 447 } 448 449 CFRelease(content); 450 451 452 if(needsBooter) { 453 contextprintf(context, kBLLogLevelVerbose, "Booter partition required at index %u\n", searchID); 454 } else { 455 contextprintf(context, kBLLogLevelVerbose, "No auxiliary booter partition required\n"); 456 } 457 458 if(needsBooter || neededSystemContent) { 459 kret = IORegistryEntryGetChildIterator(parent, kIOServicePlane, &childIterator); 460 if(kret != KERN_SUCCESS) { 461 contextprintf(context, kBLLogLevelError, "Could not get child iterator for parent\n" ); 462 return 4; 463 } 464 465 while((child = IOIteratorNext(childIterator)) != IO_OBJECT_NULL) { 466 CFStringRef childContent; 467 468 childContent = IORegistryEntryCreateCFProperty(child, CFSTR(kIOMediaContentKey), kCFAllocatorDefault, 0); 469 if(childContent && CFGetTypeID(childContent) == CFStringGetTypeID()) { 470 CFStringRef childBSDName; 471 // does it match 472 if(needsBooter && CFEqual(childContent, neededBooterContent)) { 473 CFNumberRef childPartitionID = IORegistryEntryCreateCFProperty(child, CFSTR(kIOMediaPartitionIDKey), kCFAllocatorDefault, 0); 474 475 if(childPartitionID && (CFGetTypeID(childPartitionID) == CFNumberGetTypeID()) && CFEqual(childPartitionID, neededBooterPartitionNum)) { 476 childBSDName = IORegistryEntryCreateCFProperty(child, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0); 477 if(childBSDName && (CFGetTypeID(childBSDName) == CFStringGetTypeID())) { 478 if(!CFArrayContainsValue(booterPartitions,CFRangeMake(0, CFArrayGetCount(booterPartitions)), childBSDName)) { 479 CFArrayAppendValue(booterPartitions, childBSDName); 480 contextprintf(context, kBLLogLevelVerbose, "Booter partition found\n" ); 481 } 482 } 483 484 if (childBSDName) { 485 CFRelease(childBSDName); 486 } 487 } 488 } else if(neededSystemContent && CFEqual(childContent, neededSystemContent)) { 489 childBSDName = IORegistryEntryCreateCFProperty(child, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0); 490 if(childBSDName && (CFGetTypeID(childBSDName) == CFStringGetTypeID())) { 491 if(!CFArrayContainsValue(systemPartitions,CFRangeMake(0, CFArrayGetCount(systemPartitions)), childBSDName)) { 492 CFArrayAppendValue(systemPartitions, childBSDName); 493 contextprintf(context, kBLLogLevelVerbose, "System partition found\n" ); 494 } 495 } 496 497 if (childBSDName) { 498 CFRelease(childBSDName); 499 } 500 } 501 } 502 503 if(childContent) { 504 CFRelease(childContent); 505 } 506 507 IOObjectRelease(child); 508 } 509 510 IOObjectRelease(childIterator); 511 512 } 513 514 IOObjectRelease(parent); 515 516 return 0; 517} 518 519 520#ifndef kIOPropertyPhysicalInterconnectTypePCIExpress 521#define kIOPropertyPhysicalInterconnectTypePCIExpress "PCI-Express" 522#endif 523 524static bool _isPreferredSystemPartition(BLContextPtr context, io_service_t service) 525{ 526 CFDictionaryRef protocolCharacteristics; 527 bool foundOne = false; 528 529 protocolCharacteristics = IORegistryEntrySearchCFProperty(service, 530 kIOServicePlane, 531 CFSTR(kIOPropertyProtocolCharacteristicsKey), 532 kCFAllocatorDefault, 533 kIORegistryIterateRecursively| 534 kIORegistryIterateParents); 535 536 if(protocolCharacteristics && CFGetTypeID(protocolCharacteristics) == CFDictionaryGetTypeID()) { 537 CFStringRef interconnect = CFDictionaryGetValue(protocolCharacteristics, 538 CFSTR(kIOPropertyPhysicalInterconnectTypeKey)); 539 CFStringRef location = CFDictionaryGetValue(protocolCharacteristics, 540 CFSTR(kIOPropertyPhysicalInterconnectLocationKey)); 541 542 if(interconnect && location && CFGetTypeID(interconnect) == CFStringGetTypeID() && CFGetTypeID(location) == CFStringGetTypeID()) { 543 if( ( CFEqual(interconnect,CFSTR(kIOPropertyPhysicalInterconnectTypeATA)) 544 || CFEqual(interconnect,CFSTR(kIOPropertyPhysicalInterconnectTypeSerialATA)) 545 || CFEqual(interconnect,CFSTR(kIOPropertyPhysicalInterconnectTypePCIExpress)) 546 || CFEqual(interconnect,CFSTR(kIOPropertyPhysicalInterconnectTypePCI))) 547 && CFEqual(location, CFSTR(kIOPropertyInternalKey))) { 548 // OK, found an internal ESP 549 foundOne = true; 550 } 551 552 } 553 554 } 555 if(protocolCharacteristics) CFRelease(protocolCharacteristics); 556 557 return foundOne; 558 559} 560 561bool isPreferredSystemPartition(BLContextPtr context, CFStringRef bsdName) 562{ 563 CFMutableDictionaryRef matching; 564 io_service_t service; 565 bool ret; 566 567 matching = IOServiceMatching(kIOMediaClass); 568 CFDictionarySetValue(matching, CFSTR(kIOBSDNameKey), bsdName); 569 570 571 service = IOServiceGetMatchingService(kIOMasterPortDefault, 572 matching); 573 574 if(service == IO_OBJECT_NULL) { 575 return false; 576 } 577 578 ret = _isPreferredSystemPartition(context, service); 579 580 IOObjectRelease(service); 581 582 return ret; 583} 584 585 586// search for all ESPs on the system. Once we find them, only add internal 587// interconnects on a SATA/PATA/PCI bus 588// See comments in BLCreateBooterInformationDictionary for the odd logic 589// of where any found preferred ESPs go. We find *all* of them and don't just 590// quit after one. 591static int addPreferredSystemPartitionInfo(BLContextPtr context, 592 CFMutableArrayRef systemPartitions, 593 bool foundPreferred) 594{ 595 596 CFMutableDictionaryRef matching; 597 kern_return_t kret; 598 io_iterator_t iter; 599 io_service_t service; 600 CFStringRef bsdName; 601 602 matching = IOServiceMatching(kIOMediaClass); 603 CFDictionarySetValue(matching, CFSTR(kIOMediaContentKey), CFSTR("C12A7328-F81F-11D2-BA4B-00A0C93EC93B")); 604 605 kret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching, &iter); 606 if (kret != KERN_SUCCESS) { 607 contextprintf(context, kBLLogLevelVerbose, "No preferred system partitions found\n" ); 608 return 0; 609 } 610 611 while ((service = IOIteratorNext(iter)) != IO_OBJECT_NULL) { 612 if (_isPreferredSystemPartition(context, service)) { 613 bsdName = IORegistryEntryCreateCFProperty(service, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0); 614 if (bsdName && (CFGetTypeID(bsdName) == CFStringGetTypeID())) { 615 contextprintf(context, kBLLogLevelVerbose, "Preferred system partition found: %s\n", BLGetCStringDescription(bsdName)); 616 if (CFArrayGetFirstIndexOfValue(systemPartitions, CFRangeMake(0, CFArrayGetCount(systemPartitions)), bsdName) == kCFNotFound) { 617 // this is a new preferred ESP. If there isn't already a preferred one in the array, 618 // put this one at the front. Otherwise put it second. 619 CFArrayInsertValueAtIndex(systemPartitions, foundPreferred ? 1 : 0, bsdName); 620 foundPreferred = true; 621 } 622 } 623 if (bsdName) CFRelease(bsdName); 624 IOObjectRelease(service); 625 } 626 IOObjectRelease(service); 627 } 628 IOObjectRelease(iter); 629 630 return 0; 631} 632 633