1/* 2 * Copyright (c) 2001-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 * handleInfo.c 25 * bless 26 * 27 * Created by Shantonu Sen <ssen@apple.com> on Thu Dec 6 2001. 28 * Copyright (c) 2001-2007 Apple Inc. All Rights Reserved. 29 * 30 * $Id: handleInfo.c,v 1.45 2006/06/24 00:46:37 ssen Exp $ 31 * 32 * 33 */ 34 35#include <stdlib.h> 36#include <stdio.h> 37#include <unistd.h> 38#include <string.h> 39 40#include <sys/stat.h> 41#include <sys/mount.h> 42#include <sys/param.h> 43 44#include <sys/socket.h> 45#include <net/if.h> 46#include <arpa/nameser.h> 47#include <netinet/in.h> 48#include <arpa/inet.h> 49 50#include "enums.h" 51#include "structs.h" 52 53#include "bless.h" 54#include "bless_private.h" 55#include "protos.h" 56 57#include <IOKit/storage/IOMedia.h> 58#include <IOKit/IOBSD.h> 59#include <CoreFoundation/CoreFoundation.h> 60 61static int interpretEFIString(BLContextPtr context, CFStringRef efiString, 62 char *bootdevice); 63 64static void addElements(const void *key, const void *value, void *context); 65 66static int findBootRootAggregate(BLContextPtr context, char *memberPartition, char *bootRootDevice, int deviceLen); 67 68 69 70/* 8 words of "finder info" in volume 71 * 0 & 1 often set to blessed system folder 72 * boot blocks contain name of system file, name of shell app, and startup app 73 * starting w/sys8 added file OpenFolderListDF ... which wins open at mount 74 * there are per-file/folder "finder info" fields such as invis, location, etc 75 * "next-folder in "open folder list" chain ... first item came from word 2 76 * 3 & 5 co-opted in the X timeframe to store dirID's of dual-install sysF's 77 * 6 & 7 is the vsdb stuff (64 bits) to see if sysA has seen diskB 78 * 79 * 0 is blessed system folder 80 * 1 is blessed system file for EFI systems 81 * 2 is first link in linked list of folders to open at mount (deprecated) 82 * (9 and X are supposed to honor this if set and ignore OpenFolderListDF) 83 * 3 OS 9 blessed system folder (maybe OS X?) 84 * 4 thought to be unused (reserved for Finder, once was PowerTalk Inbox) 85 * 5 OS X blessed system folder (maybe OS 9?) 86 * 6 & 7 are 64 bit volume identifier (high 32 bits in 6; low in 7) 87 */ 88 89#define MISSINGMSG "<missing>" 90static const char *messages[7][2] = { 91 92 { "No Blessed System Folder", "Blessed System Folder is " }, /* 0 */ 93 { "No Blessed System File", "Blessed System File is " }, 94 { "Open-folder linked list empty", "1st dir in open-folder list is " }, 95 { "No alternate OS blessed file/folder", "Alternate OS blessed file/folder is " }, /* 3 */ 96 { "Unused field unset", "Thought-to-be-unused field points to " }, 97 { "No OS 9 + X blessed X folder", "OS X blessed folder is " }, /* 5 */ 98 { "64-bit VSDB volume id not present", "64-bit VSDB volume id: " } 99 100}; 101 102int modeInfo(BLContextPtr context, struct clarg actargs[klast]) { 103 int ret; 104 CFDictionaryRef dict; 105 struct statfs sb; 106 CFMutableDictionaryRef allInfo = NULL; 107 int isHFS = 0; 108 109 if(!actargs[kinfo].hasArg || actargs[kgetboot].present) { 110 char currentString[1024]; 111 char currentDev[1024]; // may contain URLs like bsdp://foo 112 BLPreBootEnvType preboot; 113 114 ret = BLGetPreBootEnvironmentType(context, &preboot); 115 if(ret) 116 return 1; 117 118 if (preboot == kBLPreBootEnvType_EFI) { 119 CFStringRef efibootdev = NULL; 120 121 ret = BLCopyEFINVRAMVariableAsString(context, 122 CFSTR("efi-boot-device"), 123 &efibootdev); 124 125 if(ret || efibootdev == NULL) { 126 blesscontextprintf(context, kBLLogLevelError, 127 "Can't access \"efi-boot-device\" NVRAM variable\n"); 128 return 1; 129 } 130 131 blesscontextprintf(context, kBLLogLevelVerbose, "Current EFI boot device string is: '%s'\n", 132 BLGetCStringDescription(efibootdev)); 133 134 ret = BLValidateXMLBootOption(context, 135 CFSTR("efi-boot-device"), 136 CFSTR("efi-boot-device-data")); 137 if(ret) { 138 CFRelease(efibootdev); 139 blesscontextprintf(context, kBLLogLevelError, 140 "XML representation doesn't match true boot preference\n"); 141 return 2; 142 } 143 144 ret = interpretEFIString(context, efibootdev, currentDev); 145 if(ret) { 146 CFRelease(efibootdev); 147 blesscontextprintf(context, kBLLogLevelError, 148 "Can't interpet EFI boot device\n"); 149 return 2; 150 } 151 152 CFRelease(efibootdev); 153 } else { 154 blesscontextprintf(context, kBLLogLevelError, "Unknown preboot environment\n"); 155 return 1; 156 } 157 158 // Check for Boot!=Root for physical disk boot paths 159 if (0 == strncmp(currentDev, "/dev/", 5)) { 160 char parentDev[MNAMELEN]; 161 uint32_t partNum; 162 BLPartitionType mapType; 163 io_service_t service = IO_OBJECT_NULL; 164 CFStringRef contentHint; 165 166 ret = BLGetIOServiceForDeviceName(context, currentDev + 5, &service); 167 if (ret) { 168 blesscontextprintf(context, kBLLogLevelError, 169 "Can't get IOService for %s\n", currentDev); 170 return 3; 171 } 172 173 contentHint = IORegistryEntryCreateCFProperty(service, CFSTR(kIOMediaContentKey), kCFAllocatorDefault, 0); 174 175 if (contentHint && CFGetTypeID(contentHint) == CFStringGetTypeID()) { 176 bool doSearch = false; 177 char booterPart[MNAMELEN]; 178 179 ret = BLGetParentDeviceAndPartitionType(context, currentDev, 180 parentDev, 181 &partNum, 182 &mapType); 183 if (ret) { 184 blesscontextprintf(context, kBLLogLevelError, 185 "Can't determine parent media for %s\n", currentDev); 186 return 3; 187 } 188 189 switch(mapType) { 190 case kBLPartitionType_APM: 191 if (CFEqual(contentHint, CFSTR("Apple_Boot"))) { 192 doSearch = true; 193 snprintf(booterPart, sizeof(booterPart), "%ss%u", parentDev, partNum+1); 194 } 195 break; 196 case kBLPartitionType_GPT: 197 if (CFEqual(contentHint, CFSTR("426F6F74-0000-11AA-AA11-00306543ECAC"))) { 198 doSearch = true; 199 snprintf(booterPart, sizeof(booterPart), "%ss%u", parentDev, partNum-1); 200 } 201 break; 202 default: 203 blesscontextprintf(context, kBLLogLevelVerbose, 204 "Partition map type does not support Boot!=Root\n"); 205 break; 206 } 207 208 if (doSearch) { 209 ret = findBootRootAggregate(context, booterPart, currentDev, sizeof currentDev); 210 if (ret) { 211 blesscontextprintf(context, kBLLogLevelError, 212 "Failed to find Boot!=Root aggregate media for %s\n", currentDev); 213 return 3; 214 } 215 } 216 } 217 218 219 if (contentHint) CFRelease(contentHint); 220 IOObjectRelease(service); 221 222 } 223 224 if( actargs[kgetboot].present) { 225 if(actargs[kplist].present) { 226 CFStringRef vol = CFStringCreateWithCString(kCFAllocatorDefault, 227 currentDev, 228 kCFStringEncodingUTF8); 229 CFMutableDictionaryRef dict = NULL; 230 CFDataRef tempData = NULL; 231 232 dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 233 &kCFTypeDictionaryKeyCallBacks, 234 &kCFTypeDictionaryValueCallBacks); 235 CFDictionaryAddValue(dict, CFSTR("Boot Volume"), vol); 236 CFRelease(vol); 237 238 tempData = CFPropertyListCreateXMLData(kCFAllocatorDefault, dict); 239 240 write(fileno(stdout), CFDataGetBytePtr(tempData), CFDataGetLength(tempData)); 241 242 CFRelease(tempData); 243 CFRelease(dict); 244 245 } else { 246 blesscontextprintf(context, kBLLogLevelNormal, "%s\n", currentDev); 247 } 248 return 0; 249 } 250 251 // only look at mountpoints if it looks like a dev node 252 if (0 == strncmp(currentDev, "/dev/", 5)) { 253 struct statfs *mnts; 254 int vols; 255 256 vols = getmntinfo(&mnts, MNT_NOWAIT); 257 if(vols == -1) { 258 blesscontextprintf(context, kBLLogLevelError, "Error gettings mounts\n" ); 259 return 1; 260 } 261 262 while(--vols >= 0) { 263 struct statfs sb; 264 265 // somewhat redundant, but blsustatfs will canonicalize the mount device 266 if(0 != blsustatfs(mnts[vols].f_mntonname, &sb)) { 267 continue; 268 } 269 270 if(strncmp(sb.f_mntfromname, currentDev, strlen(currentDev)+1) == 0) { 271 blesscontextprintf(context, kBLLogLevelVerbose, "mount: %s\n", mnts[vols].f_mntonname ); 272 strlcpy(actargs[kinfo].argument, mnts[vols].f_mntonname, kMaxArgLength); 273 actargs[kinfo].hasArg = 1; 274 break; 275 } 276 277 } 278 } 279 280 if(!actargs[kinfo].hasArg) { 281 blesscontextprintf(context, kBLLogLevelError, 282 "Volume for path %s is not available\n", 283 currentString); 284 return 2; 285 } 286 } 287 288 289 ret = BLGetCommonMountPoint(context, actargs[kinfo].argument, "", actargs[kmount].argument); 290 if(ret) { 291 blesscontextprintf(context, kBLLogLevelError, "Can't get mount point for %s\n", actargs[kinfo].argument ); 292 return 1; 293 } 294 295 ret = blsustatfs(actargs[kmount].argument, &sb); 296 if(ret) { 297 blesscontextprintf(context, kBLLogLevelError, "Can't get device for %s\n", actargs[kmount].argument ); 298 return 1; 299 300 } 301 302 ret = BLIsMountHFS(context, actargs[kmount].argument, &isHFS); 303 if(ret) { 304 blesscontextprintf(context, kBLLogLevelError, "Could not determine filesystem of %s\n", actargs[kmount].argument ); 305 return 1; 306 } 307 308 if(isHFS) { 309 ret = BLCreateVolumeInformationDictionary(context, actargs[kmount].argument, 310 &dict); 311 if(ret) { 312 blesscontextprintf(context, kBLLogLevelError, "Can't print Finder information\n" ); 313 return 1; 314 } 315 316 allInfo = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, dict); 317 CFRelease(dict); 318 dict = NULL; 319 } else { 320 allInfo = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 321 } 322 323 324 ret = BLCreateBooterInformationDictionary(context, sb.f_mntfromname + 5, &dict); 325 if(ret) { 326 blesscontextprintf(context, kBLLogLevelError, "Can't get booter information\n" ); 327 return 3; 328 } 329 330 CFDictionaryApplyFunction(dict, addElements, (void *)allInfo); 331 CFRelease(dict); 332 333 dict = (CFDictionaryRef)allInfo; 334 335 if(actargs[kplist].present) { 336 CFDataRef tempData = NULL; 337 338 tempData = CFPropertyListCreateXMLData(kCFAllocatorDefault, dict); 339 340 write(fileno(stdout), CFDataGetBytePtr(tempData), CFDataGetLength(tempData)); 341 342 CFRelease(tempData); 343 344 } else if(isHFS) { 345 CFArrayRef finfo = CFDictionaryGetValue(dict, CFSTR("Finder Info")); 346 int j; 347 CFNumberRef vsdbref; 348 uint64_t vsdb; 349 350 for(j = 0; j < (8-2); j++) { 351 CFDictionaryRef word = CFArrayGetValueAtIndex(finfo, j); 352 CFNumberRef dirID = CFDictionaryGetValue(word, CFSTR("Directory ID")); 353 CFStringRef path = CFDictionaryGetValue(word, CFSTR("Path")); 354 uint32_t dirint; 355 char cpath[MAXPATHLEN]; 356 357 if(!CFNumberGetValue(dirID, kCFNumberSInt32Type, &dirint)) { 358 continue; 359 } 360 361 if(dirint > 0 && CFStringGetLength(path) == 0) { 362 strlcpy(cpath, MISSINGMSG, sizeof cpath); 363 } else { 364 if(!CFStringGetCString(path, cpath, MAXPATHLEN, kCFStringEncodingUTF8)) { 365 continue; 366 } 367 } 368 369 blesscontextprintf(context, kBLLogLevelNormal, 370 "finderinfo[%i]: %6u => %s%s\n", j, dirint, 371 messages[j][dirint > 0], cpath); 372 373 } 374 375 376 vsdbref = CFDictionaryGetValue(dict, CFSTR("VSDB ID")); 377 CFNumberGetValue(vsdbref, kCFNumberSInt64Type, &vsdb); 378 379 380 blesscontextprintf(context, kBLLogLevelNormal, "%s 0x%016llX\n", messages[6][1], 381 vsdb); 382 383 } 384 385 CFRelease(dict); 386 387 return 0; 388} 389 390static int interpretEFIString(BLContextPtr context, CFStringRef efiString, 391 char *bootdevice) 392{ 393 char interface[IF_NAMESIZE]; 394 char host[NS_MAXDNAME]; 395 char path[1024]; 396 int ret; 397 398 ret = BLInterpretEFIXMLRepresentationAsDevice(context, 399 efiString, 400 path, 401 sizeof path); 402 if(ret == 0) { 403 blesscontextprintf(context, kBLLogLevelVerbose, "Disk boot device detected\n" ); 404 405 sprintf(bootdevice, "/dev/%s", path); 406 407 return 0; 408 } else { 409 BLNetBootProtocolType protocol; 410 411 ret = BLInterpretEFIXMLRepresentationAsNetworkPath(context, 412 efiString, 413 &protocol, 414 interface, 415 host, 416 path); 417 418 if(ret == 0) { 419 blesscontextprintf(context, kBLLogLevelVerbose, "Network boot device detected\n" ); 420 421 if(strlen(path) > 0) { 422 sprintf(bootdevice, "tftp://%s@%s/%s", interface, host, path); 423 } else { 424 sprintf(bootdevice, "%s://%s@%s", 425 (protocol == kBLNetBootProtocol_PXE ? "pxe" : "bsdp"), 426 interface, host); 427 } 428 429 return 0; 430 } else { 431 ret = BLInterpretEFIXMLRepresentationAsLegacyDevice(context, 432 efiString, 433 path, 434 sizeof path); 435 if(ret == 0) { 436 blesscontextprintf(context, kBLLogLevelVerbose, "Legacy boot device detected\n" ); 437 438 sprintf(bootdevice, "/dev/%s", path); 439 440 return 0; 441 } 442 } 443 } 444 445 blesscontextprintf(context, kBLLogLevelError, "Could not interpret boot device as either network or disk\n" ); 446 447 return 1; 448} 449 450// stolen from BLGetDeviceForOFPath 451static int isBootRootPath(BLContextPtr context, mach_port_t iokitPort, io_service_t member, 452 CFDictionaryRef raidEntry) 453{ 454 CFStringRef path; 455 io_string_t cpath; 456 io_service_t service; 457 458 path = CFDictionaryGetValue(raidEntry, CFSTR(kIOBootDevicePathKey)); 459 if(path == NULL) return 0; 460 461 if(!CFStringGetCString(path,cpath,sizeof(cpath),kCFStringEncodingUTF8)) 462 return 0; 463 464 contextprintf(context, kBLLogLevelVerbose, "Comparing member to %s", cpath); 465 466 service = IORegistryEntryFromPath(iokitPort, cpath); 467 if(service == 0) { 468 contextprintf(context, kBLLogLevelVerbose, "\nCould not find service\n"); 469 return 0; 470 } 471 472 if(IOObjectIsEqualTo(service, member)) { 473 contextprintf(context, kBLLogLevelVerbose, "\tEQUAL\n"); 474 IOObjectRelease(service); 475 return 1; 476 } else { 477 contextprintf(context, kBLLogLevelVerbose, "\tNOT EQUAL\n"); 478 } 479 480 IOObjectRelease(service); 481 482 return 0; 483} 484 485 486 487static int findBootRootAggregate(BLContextPtr context, char *memberPartition, char *bootRootDevice, int deviceLen) 488{ 489 io_service_t member = IO_OBJECT_NULL, testmedia = IO_OBJECT_NULL; 490 io_iterator_t iter; 491 kern_return_t kret; 492 int ret; 493 bool foundBootRoot = false; 494 CFStringRef memberContent = NULL; 495 496 ret = BLGetIOServiceForDeviceName(context, memberPartition + 5, &member); 497 if (ret) { 498 blesscontextprintf(context, kBLLogLevelError, 499 "Can't get IOService for %s\n", memberPartition); 500 return 3; 501 } 502 503 kret = IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching(kIOMediaClass), &iter); 504 if(kret != KERN_SUCCESS) { 505 IOObjectRelease(member); 506 contextprintf(context, kBLLogLevelVerbose, "Could not find any media devices on the system\n"); 507 return 2; 508 } 509 510 while((testmedia = IOIteratorNext(iter))) { 511 CFTypeRef data = NULL; 512 io_string_t iopath; 513 514 if(0 == IORegistryEntryGetPath(testmedia, kIOServicePlane, iopath)) { 515 contextprintf(context, kBLLogLevelVerbose, "Checking %s\n", iopath); 516 } 517 518 data = IORegistryEntrySearchCFProperty( testmedia, 519 kIOServicePlane, 520 CFSTR(kIOBootDeviceKey), 521 kCFAllocatorDefault, 522 kIORegistryIterateRecursively| 523 kIORegistryIterateParents); 524 525 if(data) { 526 if (CFGetTypeID(data) == CFArrayGetTypeID()) { 527 CFIndex i, count = CFArrayGetCount(data); 528 for(i=0; i < count; i++) { 529 CFDictionaryRef ent = CFArrayGetValueAtIndex((CFArrayRef)data,i); 530 if(isBootRootPath(context, kIOMasterPortDefault, member, ent)) { 531 foundBootRoot = true; 532 break; 533 } 534 } 535 536 } else if(CFGetTypeID(data) == CFDictionaryGetTypeID()) { 537 if(isBootRootPath(context, kIOMasterPortDefault, member, (CFDictionaryRef)data)) { 538 foundBootRoot = true; 539 } 540 } 541 CFRelease(data); 542 } 543 544 if (foundBootRoot) { 545 CFStringRef bsdName; 546 547 bsdName = IORegistryEntryCreateCFProperty(testmedia, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0); 548 549 contextprintf(context, kBLLogLevelVerbose, "Found Boot!=Root aggregate media %s\n", BLGetCStringDescription(bsdName)); 550 551 strlcpy(bootRootDevice, "/dev/", deviceLen); 552 CFStringGetCString(bsdName, bootRootDevice+5, 1024-5, kCFStringEncodingUTF8); 553 554 CFRelease(bsdName); 555 556 } 557 558 IOObjectRelease(testmedia); 559 560 if (foundBootRoot) break; 561 } 562 IOObjectRelease(iter); 563 564 memberContent = IORegistryEntryCreateCFProperty(member, CFSTR(kIOMediaContentKey), kCFAllocatorDefault, 0); 565 IOObjectRelease(member); 566 567 568 // not boot root. It might still be something like UFS 569 if (!foundBootRoot) { 570 if (memberContent && (!CFEqual(memberContent, CFSTR("Apple_Boot")) && !CFEqual(memberContent, CFSTR("Apple_HFS")))) { 571 foundBootRoot = true; 572 contextprintf(context, kBLLogLevelVerbose, "Found legacy Apple_Boot media %s\n", BLGetCStringDescription(memberContent)); 573 574 strlcpy(bootRootDevice, memberPartition, deviceLen); 575 } 576 } 577 578 if (memberContent) CFRelease(memberContent); 579 580 if (foundBootRoot) { 581 return 0; 582 } else { 583 return 1; 584 } 585} 586 587static void addElements(const void *key, const void *value, void *context) 588{ 589 CFMutableDictionaryRef dict = (CFMutableDictionaryRef)context; 590 591 CFDictionaryAddValue(dict, key, value); 592} 593