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 * BLCreateEFIXMLRepresentationForPath.c 25 * bless 26 * 27 * Created by Shantonu Sen on 11/9/05. 28 * Copyright 2005-2007 Apple Inc. All Rights Reserved. 29 * 30 */ 31 32#import <IOKit/IOKitLib.h> 33#import <IOKit/IOCFSerialize.h> 34#import <IOKit/IOBSD.h> 35#import <IOKit/IOKitKeys.h> 36#import <IOKit/storage/IOMedia.h> 37 38#import <CoreFoundation/CoreFoundation.h> 39 40#include <string.h> 41#include <sys/param.h> 42#include <sys/stat.h> 43 44#include "bless.h" 45#include "bless_private.h" 46 47#if USE_DISKARBITRATION 48#include <DiskArbitration/DiskArbitration.h> 49#endif 50 51int addMatchingInfoForBSDName(BLContextPtr context, 52 mach_port_t masterPort, 53 CFMutableDictionaryRef dict, 54 const char *bsdName, 55 bool shortForm); 56 57int BLCreateEFIXMLRepresentationForPath(BLContextPtr context, 58 const char *path, 59 const char *optionalData, 60 CFStringRef *xmlString, 61 bool shortForm) 62{ 63 char fullpath[MAXPATHLEN]; 64 struct statfs sb; 65 int ret; 66 int i; 67 size_t slen; 68 mach_port_t masterPort; 69 kern_return_t kret; 70 71 CFDataRef xmlData; 72 CFMutableDictionaryRef dict; 73 CFMutableArrayRef array; 74 75 const UInt8 *xmlBuffer; 76 UInt8 *outBuffer; 77 CFIndex count; 78 79 CFStringRef pathString; 80 81 kret = IOMasterPort(MACH_PORT_NULL, &masterPort); 82 if(kret) return 1; 83 84 if(NULL == realpath(path, fullpath)) { 85 86 contextprintf(context, kBLLogLevelError, "Can't resolve full path for %s\n", 87 path); 88 return 1; 89 } 90 91 ret = blsustatfs(fullpath, &sb); 92 if(ret) { 93 contextprintf(context, kBLLogLevelError, "Can't statfs %s\n", 94 fullpath); 95 return 2; 96 } 97 98 if(0 != strncmp(fullpath, sb.f_mntonname, strlen(sb.f_mntonname))) { 99 return 3; 100 } 101 102 // if fullpath was actually the path to the mountpoint, 103 // don't add a path component to the XML dict 104 if(0 != strcmp(fullpath, sb.f_mntonname)) { 105 106 if(strlen(sb.f_mntonname) > 1) { 107 memmove(fullpath, fullpath+strlen(sb.f_mntonname), 108 strlen(fullpath)-strlen(sb.f_mntonname)+1); 109 } 110 111 slen = strlen(fullpath); 112 for(i=0; i < slen; i++) { 113 if(fullpath[i] == '/') 114 fullpath[i] = '\\'; 115 } 116 117 pathString = CFStringCreateWithCString(kCFAllocatorDefault, fullpath, kCFStringEncodingUTF8); 118 119 contextprintf(context, kBLLogLevelVerbose, "Relative path of %s is %s\n", 120 path, fullpath); 121 122 } else { 123 pathString = NULL; 124 125 contextprintf(context, kBLLogLevelVerbose, "Path to mountpoint given: %s\n", 126 fullpath); 127 } 128 129 array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 130 131 dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, 132 &kCFTypeDictionaryValueCallBacks); 133 134 ret = addMatchingInfoForBSDName(context, masterPort, dict, sb.f_mntfromname+strlen("/dev/"), shortForm); 135 if(ret) { 136 CFRelease(dict); 137 CFRelease(array); 138 return 2; 139 } 140 CFArrayAppendValue(array, dict); 141 CFRelease(dict); 142 143 if(pathString) { 144 dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, 145 &kCFTypeDictionaryValueCallBacks); 146 CFDictionaryAddValue(dict, CFSTR("IOEFIDevicePathType"), 147 CFSTR("MediaFilePath")); 148 CFDictionaryAddValue(dict, CFSTR("Path"), 149 pathString); 150 CFArrayAppendValue(array, dict); 151 CFRelease(dict); 152 153 CFRelease(pathString); 154 } 155 156 if(optionalData) { 157 CFStringRef optString = CFStringCreateWithCString(kCFAllocatorDefault, optionalData, kCFStringEncodingUTF8); 158 159 dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, 160 &kCFTypeDictionaryValueCallBacks); 161 CFDictionaryAddValue(dict, CFSTR("IOEFIBootOption"), 162 optString); 163 CFArrayAppendValue(array, dict); 164 CFRelease(dict); 165 166 CFRelease(optString); 167 } 168 169 xmlData = IOCFSerialize(array, 0); 170 CFRelease(array); 171 172 if(xmlData == NULL) { 173 contextprintf(context, kBLLogLevelError, "Can't create XML representation\n"); 174 return 2; 175 } 176 177 count = CFDataGetLength(xmlData); 178 xmlBuffer = CFDataGetBytePtr(xmlData); 179 outBuffer = calloc(count+1, sizeof(char)); // terminate 180 181 memcpy(outBuffer, xmlBuffer, count); 182 CFRelease(xmlData); 183 184 *xmlString = CFStringCreateWithCString(kCFAllocatorDefault, (const char *)outBuffer, kCFStringEncodingUTF8); 185 186 free(outBuffer); 187 188 return 0; 189} 190 191// first look up the media object, then create a 192// custom matching dictionary that should be persistent 193// from boot to boot 194int addMatchingInfoForBSDName(BLContextPtr context, 195 mach_port_t masterPort, 196 CFMutableDictionaryRef dict, 197 const char *bsdName, 198 bool shortForm) 199{ 200 io_service_t media = IO_OBJECT_NULL, checkMedia = IO_OBJECT_NULL; 201 CFStringRef uuid = NULL; 202 CFMutableDictionaryRef propDict = NULL; 203 kern_return_t kret; 204 CFStringRef lastBSDName = NULL; 205 206 lastBSDName = CFStringCreateWithCString(kCFAllocatorDefault, 207 bsdName, 208 kCFStringEncodingUTF8); 209 210 propDict = IOBSDNameMatching(masterPort, 0, bsdName); 211 CFDictionarySetValue(propDict, CFSTR(kIOProviderClassKey), CFSTR(kIOMediaClass)); 212 213 media = IOServiceGetMatchingService(masterPort, 214 propDict); 215 propDict = NULL; 216 217 if(media == IO_OBJECT_NULL) { 218 contextprintf(context, kBLLogLevelError, "Could not find object for %s\n", bsdName); 219 CFRelease(lastBSDName); 220 return 1; 221 } 222 223 uuid = IORegistryEntryCreateCFProperty(media, CFSTR(kIOMediaUUIDKey), 224 kCFAllocatorDefault, 0); 225 if(uuid == NULL) { 226 CFUUIDRef fsuuid = NULL; 227 CFStringRef fsuuidstr = NULL; 228 io_string_t path; 229#if USE_DISKARBITRATION 230 DASessionRef session = NULL; 231 DADiskRef dadisk = NULL; 232 233 contextprintf(context, kBLLogLevelVerbose, "IOMedia %s does not have a partition %s\n", 234 bsdName, kIOMediaUUIDKey); 235 236 session = DASessionCreate(kCFAllocatorDefault); 237 if(session) { 238 dadisk = DADiskCreateFromIOMedia(kCFAllocatorDefault, session, 239 media); 240 if(dadisk) { 241 CFDictionaryRef descrip = DADiskCopyDescription(dadisk); 242 if(descrip) { 243 fsuuid = CFDictionaryGetValue(descrip, kDADiskDescriptionVolumeUUIDKey); 244 245 if(fsuuid) 246 CFRetain(fsuuid); 247 CFRelease(descrip); 248 } 249 250 CFRelease(dadisk); 251 } 252 253 CFRelease(session); 254 } 255#endif // USE_DISKARBITRATION 256 257 if(fsuuid) { 258 char fsuuidCString[64]; 259 260 fsuuidstr = CFUUIDCreateString(kCFAllocatorDefault, fsuuid); 261 262 CFStringGetCString(fsuuidstr,fsuuidCString,sizeof(fsuuidCString),kCFStringEncodingUTF8); 263 264 contextprintf(context, kBLLogLevelVerbose, "DADiskRef %s has Volume UUID %s\n", 265 bsdName, fsuuidCString); 266 267 CFRelease(fsuuid); 268 } else { 269 contextprintf(context, kBLLogLevelVerbose, "IOMedia %s does not have a Volume UUID\n", 270 bsdName); 271 } 272 273 274 // we have a volume UUID, but our primary matching mechanism will be the device path 275 276 kret = IORegistryEntryGetPath(media, kIODeviceTreePlane,path); 277 if(kret) { 278 contextprintf(context, kBLLogLevelVerbose, "IOMedia %s does not have device tree path\n", 279 bsdName); 280 281 propDict = IOServiceMatching(kIOMediaClass); 282 CFDictionaryAddValue(propDict, CFSTR(kIOBSDNameKey), lastBSDName); 283 284 // add UUID as hint 285 if(fsuuidstr) 286 CFDictionaryAddValue(dict, CFSTR("BLVolumeUUID"), fsuuidstr); 287 288 } else { 289 CFStringRef blpath = CFStringCreateWithCString(kCFAllocatorDefault, path, kCFStringEncodingUTF8); 290 291 contextprintf(context, kBLLogLevelVerbose, "IOMedia %s has path %s\n", 292 bsdName, path); 293 294 propDict = IOServiceMatching(kIOMediaClass); 295 CFDictionaryAddValue(propDict, CFSTR(kIOPathMatchKey), blpath); 296 CFRelease(blpath); 297 298 // add UUID as hint 299 if(fsuuidstr) 300 CFDictionaryAddValue(dict, CFSTR("BLVolumeUUID"), fsuuidstr); 301 302 CFDictionaryAddValue(dict, CFSTR("BLLastBSDName"), lastBSDName); 303 } 304 305 if(fsuuidstr) { 306 CFRelease(fsuuidstr); 307 } 308 309 } else { 310 CFMutableDictionaryRef propMatch; 311 312 contextprintf(context, kBLLogLevelVerbose, "IOMedia %s has UUID %s\n", 313 bsdName, BLGetCStringDescription(uuid)); 314 315 propMatch = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 316 &kCFTypeDictionaryKeyCallBacks, 317 &kCFTypeDictionaryValueCallBacks); 318 CFDictionaryAddValue(propMatch, CFSTR(kIOMediaUUIDKey), uuid); 319 320 propDict = IOServiceMatching(kIOMediaClass); 321 CFDictionaryAddValue(propDict, CFSTR(kIOPropertyMatchKey), propMatch); 322 CFRelease(propMatch); 323 324 // add a hint to the top-level dict 325 CFDictionaryAddValue(dict, CFSTR("BLLastBSDName"), lastBSDName); 326 327 CFRelease(uuid); 328 } 329 330 // verify the dictionary matches 331 CFRetain(propDict); // consumed below 332 checkMedia = IOServiceGetMatchingService(masterPort, 333 propDict); 334 335 if(IO_OBJECT_NULL == checkMedia 336 || !IOObjectIsEqualTo(media, checkMedia)) { 337 contextprintf(context, kBLLogLevelVerbose, "Inconsistent registry entries for %s\n", 338 bsdName); 339 340 if(IO_OBJECT_NULL != checkMedia) IOObjectRelease(checkMedia); 341 IOObjectRelease(media); 342 CFRelease(lastBSDName); 343 CFRelease(propDict); 344 345 return 2; 346 } 347 348 IOObjectRelease(checkMedia); 349 IOObjectRelease(media); 350 351 CFDictionaryAddValue(dict, CFSTR("IOMatch"), propDict); 352 CFRelease(lastBSDName); 353 CFRelease(propDict); 354 355 if(shortForm) { 356 CFDictionaryAddValue(dict, CFSTR("IOEFIShortForm"), kCFBooleanTrue); 357 } 358 359 return 0; 360} 361