1/* 2 * Copyright (c) 2005-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 * BLUpdateRAIDBooters.c 25 * bless 26 * 27 * Created by Shantonu Sen on 1/15/05. 28 * Copyright 2005-2007 Apple Inc. All Rights Reserved. 29 * 30 * $Id: BLUpdateRAIDBooters.c,v 1.12 2006/02/20 22:49:58 ssen Exp $ 31 * 32 */ 33 34#include <stdlib.h> 35#include <unistd.h> 36#include <sys/param.h> 37 38#include <mach/mach_error.h> 39 40#include <IOKit/IOKitLib.h> 41#include <IOKit/IOBSD.h> 42#include <IOKit/IOKitKeys.h> 43#include <IOKit/storage/IOMedia.h> 44 45#include <CoreFoundation/CoreFoundation.h> 46 47#include "bless.h" 48#include "bless_private.h" 49 50#if SUPPORT_RAID 51 52#include <IOKit/storage/RAID/AppleRAIDUserLib.h> 53 54#if USE_DISKARBITRATION 55#include <DiskArbitration/DiskArbitration.h> 56#endif 57 58#define kBootPlistName "com.apple.Boot.plist" 59 60 61int updateRAIDMember(BLContextPtr context, mach_port_t iokitPort, 62 CFDictionaryRef raidEntry, CFDataRef opaqueData, 63 CFDataRef bootxData, CFDataRef labelData); 64int getExternalBooter(BLContextPtr context, 65 mach_port_t iokitPort, 66 io_service_t dataPartition, 67 io_service_t *booterPartition); 68 69int updateAppleBoot(BLContextPtr context, const char *devname, CFDataRef opaqueData, 70 CFDataRef bootxData, CFDataRef labelData); 71 72CFDataRef _createLabel(BLContextPtr context, CFStringRef name, int index); 73 74int BLUpdateRAIDBooters(BLContextPtr context, const char * device, 75 CFTypeRef bootData, 76 CFDataRef bootxData, CFDataRef labelData) 77{ 78 int ret = 0; 79 int anyFailed = 0; 80 CFDataRef xmlData = NULL; 81 CFStringRef daName = NULL; 82 83 kern_return_t kret; 84 mach_port_t ourIOKitPort; 85 86 87 xmlData = CFPropertyListCreateXMLData(kCFAllocatorDefault, bootData); 88 if(xmlData == NULL) { 89 contextprintf(context, kBLLogLevelError, "Error generating XML data\n"); 90 return 1; 91 } 92 93 contextprintf(context, kBLLogLevelVerbose, "RAID XML data is:\n%s\n", 94 CFDataGetBytePtr(xmlData)); 95 96 97 // Obtain the I/O Kit communication handle. 98 if((kret = IOMasterPort(bootstrap_port, &ourIOKitPort)) != KERN_SUCCESS) { 99 return 2; 100 } 101 102 for(;;) { 103 // try to get a symbolic name from DA, if possible 104 if(labelData == NULL) { 105#if USE_DISKARBITRATION 106 DADiskRef disk = NULL; 107 DASessionRef session = NULL; 108 CFDictionaryRef props = NULL; 109 110 session = DASessionCreate(kCFAllocatorDefault); 111 if(session == NULL) { 112 contextprintf(context, kBLLogLevelVerbose, "Can't connect to DiskArb\n"); 113 break; 114 } 115 116 disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, device+5); 117 if(disk == NULL) { 118 CFRelease(session); 119 contextprintf(context, kBLLogLevelVerbose, "Can't create DADisk for %s\n", 120 device + 5); 121 break; 122 } 123 124 props = DADiskCopyDescription(disk); 125 if(props == NULL) { 126 CFRelease(session); 127 CFRelease(disk); 128 contextprintf(context, kBLLogLevelVerbose, "Can't get properties for %s\n", 129 device + 5); 130 break; 131 } 132 133 daName = CFDictionaryGetValue(props, kDADiskDescriptionVolumeNameKey); 134 if(daName) CFRetain(daName); 135 136 CFRelease(props); 137 CFRelease(disk); 138 CFRelease(session); 139#else // !USE_DISKARBITRATION 140 daName = CFSTR("RAID"); 141#endif // !USE_DISKARBITRATION 142 } 143 break; 144 } 145 146 147 if(CFGetTypeID(bootData) == CFArrayGetTypeID()) { 148 CFIndex i, count = CFArrayGetCount(bootData); 149 150 for(i=0; i < count; i++) { 151 CFDictionaryRef dict = CFArrayGetValueAtIndex(bootData,i); 152 CFDataRef tempLabel = NULL; 153 154 if(!labelData && daName) { 155 tempLabel = _createLabel(context, daName, i+1); 156 } 157 158 ret = updateRAIDMember(context, ourIOKitPort, dict, xmlData, 159 bootxData, labelData ? labelData : tempLabel); 160 if(ret) { 161 anyFailed = 1; 162 // keep going to update other booters 163 } 164 165 if(tempLabel) CFRelease(tempLabel); 166 } 167 } else { 168 CFDataRef tempLabel = NULL; 169 170 if(!labelData && daName) { 171 tempLabel = _createLabel(context, daName, 1); 172 } 173 174 ret = updateRAIDMember(context, ourIOKitPort, 175 (CFDictionaryRef)bootData, xmlData, 176 bootxData, labelData ? labelData : tempLabel); 177 if(ret) { 178 anyFailed = 1; 179 // keep going to update other booters 180 } 181 182 if(tempLabel) CFRelease(tempLabel); 183} 184 CFRelease(xmlData); 185 186 if(anyFailed) { 187 return 1; 188 } else { 189 return 0; 190 } 191} 192 193int updateRAIDMember(BLContextPtr context, mach_port_t iokitPort, 194 CFDictionaryRef raidEntry, CFDataRef opaqueData, 195 CFDataRef bootxData, CFDataRef labelData) 196{ 197 int ret; 198 CFStringRef path; 199 io_string_t cpath; 200 io_service_t service; 201 io_service_t booter; 202 203 CFStringRef name = NULL; 204 CFStringRef content = NULL; 205 char cname[MAXPATHLEN]; 206 207 208 path = CFDictionaryGetValue(raidEntry, CFSTR(kIOBootDevicePathKey)); 209 if(path == NULL) return 1; 210 211 if(!CFStringGetCString(path,cpath,sizeof(cpath),kCFStringEncodingUTF8)) 212 return 2; 213 214 contextprintf(context, kBLLogLevelVerbose, "Updating booter data for %s\n", cpath); 215 216 service = IORegistryEntryFromPath(iokitPort, cpath); 217 if(service == 0) { 218 contextprintf(context, kBLLogLevelVerbose, "Could not find service for \"%s\"\n", cpath); 219 return 3; 220 } 221 222 content = (CFStringRef)IORegistryEntryCreateCFProperty( 223 service, 224 CFSTR(kIOMediaContentKey), 225 kCFAllocatorDefault, 226 0); 227 228 if(content) { 229 if(CFStringGetTypeID() == CFGetTypeID(content) 230 && CFEqual(content, CFSTR("Apple_Boot_RAID"))) { 231 232 contextprintf(context, kBLLogLevelVerbose, "Member at \"%s\" is RAIDv1 Apple_Boot_RAID partition. Ignoring...\n", cpath); 233 234 CFRelease(content); 235 IOObjectRelease(service); 236 return 0; 237 } 238 239 CFRelease(content); 240 } 241 242 ret = getExternalBooter(context, iokitPort, service, &booter); 243 if(ret) { 244 IOObjectRelease(service); 245 return 4; 246 } 247 248 name = (CFStringRef)IORegistryEntryCreateCFProperty( 249 booter, 250 CFSTR(kIOBSDNameKey), 251 kCFAllocatorDefault, 252 0); 253 254 if(!CFStringGetCString(name, cname, sizeof(cname), kCFStringEncodingUTF8)) { 255 CFRelease(name); 256 IOObjectRelease(booter); 257 IOObjectRelease(service); 258 return 5; 259 } 260 261 CFRelease(name); 262 263 contextprintf(context, kBLLogLevelVerbose, "Booter partition is %s\n", cname); 264 ret = updateAppleBoot(context, cname, opaqueData, 265 bootxData, labelData); 266 if(ret) { 267 IOObjectRelease(booter); 268 IOObjectRelease(service); 269 return 6; 270 } 271 272 IOObjectRelease(booter); 273 IOObjectRelease(service); 274 275 return 0; 276} 277 278int updateAppleBoot(BLContextPtr context, const char *devname, CFDataRef opaqueData, 279 CFDataRef bootxData, CFDataRef labelData) 280{ 281 282 int status; 283 char device[MAXPATHLEN]; 284 BLUpdateBooterFileSpec *spec = NULL; 285 BLUpdateBooterFileSpec *pspec, *xspec, *lspec1, *lspec2; 286 int32_t specCount = 0, currentCount = 0; 287 288 pspec = xspec = lspec1 = lspec2 = NULL; 289 290 sprintf(device, "/dev/%s", devname); 291 292 specCount = 1; // plist 293 if(labelData) specCount += 2; 294 if(bootxData) specCount += 1; 295 296 spec = calloc(specCount, sizeof(spec[0])); 297 298 299 pspec = &spec[0]; 300 301 pspec->version = 0; 302 pspec->reqType = 0; // no type 303 pspec->reqCreator = 0; // no type 304 pspec->reqParentDir = 0; 305 pspec->reqFilename = kBootPlistName; 306 pspec->payloadData = opaqueData; 307 pspec->postType = 0; // no type 308 pspec->postCreator = 0; // no type 309 pspec->foundFile = 0; 310 pspec->updatedFile = 0; 311 312 currentCount++; 313 314 if(labelData) { 315 lspec1 = &spec[currentCount]; 316 lspec2 = &spec[currentCount+1]; 317 318 lspec1->version = 0; 319 lspec1->reqType = kBL_OSTYPE_PPC_TYPE_OFLABEL; 320 lspec1->reqCreator = kBL_OSTYPE_PPC_CREATOR_CHRP; 321 lspec1->reqParentDir = 0; 322 lspec1->reqFilename = NULL; 323 lspec1->payloadData = labelData; 324 lspec1->postType = 0; // no type 325 lspec1->postCreator = 0; // no type 326 lspec1->foundFile = 0; 327 lspec1->updatedFile = 0; 328 329 lspec2->version = 0; 330 lspec2->reqType = kBL_OSTYPE_PPC_TYPE_OFLABEL_PLACEHOLDER; 331 lspec2->reqCreator = kBL_OSTYPE_PPC_CREATOR_CHRP; 332 lspec2->reqParentDir = 0; 333 lspec2->reqFilename = NULL; 334 lspec2->payloadData = labelData; 335 lspec2->postType = kBL_OSTYPE_PPC_TYPE_OFLABEL; 336 lspec2->postCreator = 0; // no type 337 lspec2->foundFile = 0; 338 lspec2->updatedFile = 0; 339 340 341 currentCount += 2; 342 } 343 344 if(bootxData) { 345 xspec = &spec[currentCount]; 346 347 xspec->version = 0; 348 xspec->reqType = kBL_OSTYPE_PPC_TYPE_BOOTX; 349 xspec->reqCreator = kBL_OSTYPE_PPC_CREATOR_CHRP; 350 xspec->reqParentDir = 0; 351 xspec->reqFilename = NULL; 352 xspec->payloadData = bootxData; 353 xspec->postType = 0; // no type 354 xspec->postCreator = 0; // no type 355 xspec->foundFile = 0; 356 xspec->updatedFile = 0; 357 358 currentCount++; 359 } 360 361 status = BLUpdateBooter(context, device, spec, specCount); 362 if(status) { 363 contextprintf(context, kBLLogLevelError, "Error enumerating HFS+ volume\n"); 364 return 1; 365 } 366 367 if(bootxData) { 368 if(!(xspec->foundFile)) { 369 contextprintf(context, kBLLogLevelError, "No pre-existing BootX found in HFS+ volume\n"); 370 return 2; 371 } 372 373 if(!(xspec->updatedFile)) { 374 contextprintf(context, kBLLogLevelError, "BootX was not updated\n"); 375 return 3; 376 } 377 } 378 379 if(labelData) { 380 if(!(lspec1->foundFile || lspec2->foundFile)) { 381 contextprintf(context, kBLLogLevelError, "No pre-existing OF label found in HFS+ volume\n"); 382 return 2; 383 } 384 if(!(lspec1->updatedFile || lspec2->updatedFile)) { 385 contextprintf(context, kBLLogLevelError, "OF label was not updated\n"); 386 return 3; 387 } 388 } 389 390 if(!pspec->foundFile) { 391 contextprintf(context, kBLLogLevelError, "No pre-existing Plist found in HFS+ volume\n"); 392 return 1; 393 } 394 if(!pspec->updatedFile) { 395 contextprintf(context, kBLLogLevelError, "Plist was not updated\n"); 396 return 2; 397 } 398 399 return 0; 400} 401 402CFDataRef _createLabel(BLContextPtr context, CFStringRef name, int index) 403{ 404 CFDataRef newLabel = NULL; 405 char label[MAXPATHLEN]; 406 int ret; 407 CFStringRef nameWithNum = NULL; 408 409 nameWithNum = CFStringCreateWithFormat(kCFAllocatorDefault,NULL, 410 CFSTR("%@ %d"), name, index); 411 if(nameWithNum == NULL) 412 return NULL; 413 414 if(!CFStringGetCString(nameWithNum, label, sizeof(label), 415 kCFStringEncodingUTF8)) { 416 CFRelease(nameWithNum); 417 return NULL; 418 } 419 420 CFRelease(nameWithNum); 421 422 ret = BLGenerateLabelData(context, label, kBitmapScale_1x, &newLabel); 423 424 if(ret) { 425 return NULL; 426 } else { 427 return newLabel; 428 } 429} 430 431#endif // SUPPORT_RAID 432