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 * BLInterpretEFIXMLRepresentationAsDevice.c 25 * bless 26 * 27 * Created by Shantonu Sen on 12/2/05. 28 * Copyright 2005-2007 Apple Inc. All Rights Reserved. 29 * 30 */ 31 32#include <IOKit/IOKitLib.h> 33#include <IOKit/IOCFUnserialize.h> 34#include <IOKit/storage/IOMedia.h> 35#include <IOKit/IOBSD.h> 36 37#include "bless.h" 38#include "bless_private.h" 39 40#if USE_DISKARBITRATION 41#include <DiskArbitration/DiskArbitration.h> 42#endif 43 44static int checkForMatch(BLContextPtr context, CFDictionaryRef dict, 45 char *bsdName, int bsdNameLen); 46 47static CFUUIDRef copyVolUUIDFromDiskArb(BLContextPtr context, 48 CFStringRef bsdName); 49 50int BLInterpretEFIXMLRepresentationAsDevice(BLContextPtr context, 51 CFStringRef xmlString, 52 char *bsdName, 53 int bsdNameLen) 54{ 55 CFArrayRef efiArray = NULL; 56 CFIndex count, i; 57 char buffer[1024]; 58 int foundDevice = 0; 59 60 if(!CFStringGetCString(xmlString, buffer, sizeof(buffer), kCFStringEncodingUTF8)) { 61 return 1; 62 } 63 64 efiArray = IOCFUnserialize(buffer, 65 kCFAllocatorDefault, 66 0, 67 NULL); 68 if(efiArray == NULL) { 69 contextprintf(context, kBLLogLevelError, "Could not unserialize string\n"); 70 return 2; 71 } 72 73 if(CFGetTypeID(efiArray) != CFArrayGetTypeID()) { 74 CFRelease(efiArray); 75 contextprintf(context, kBLLogLevelError, "Bad type in XML string\n"); 76 return 2; 77 } 78 79 // for each entry, see if there's a volume UUID, or if IOMatch works 80 count = CFArrayGetCount(efiArray); 81 for(i=0; i < count; i++) { 82 CFDictionaryRef dict = CFArrayGetValueAtIndex(efiArray, i); 83 84 if(CFGetTypeID(dict) != CFDictionaryGetTypeID()) { 85 CFRelease(efiArray); 86 contextprintf(context, kBLLogLevelError, "Bad type in XML string\n"); 87 return 2; 88 } 89 90 if(checkForMatch(context, dict, bsdName, bsdNameLen)) { 91 foundDevice = 1; 92 break; 93 } 94 } 95 96 CFRelease(efiArray); 97 98 if(!foundDevice) { 99 contextprintf(context, kBLLogLevelVerbose, "Could not find disk device for string\n"); 100 return 4; 101 } 102 103 return 0; 104} 105 106 107static int checkForMatch(BLContextPtr context, CFDictionaryRef dict, 108 char *bsdName, int bsdNameLen) 109{ 110 CFStringRef fsuuid; 111 CFUUIDRef uuid = NULL; 112 io_service_t service; 113 int foundDevice = 0; 114 115 // first check for volume UUID. if it's present at all, it's preferred 116 117 fsuuid = CFDictionaryGetValue(dict, CFSTR("BLVolumeUUID")); 118 if(fsuuid && CFGetTypeID(fsuuid) == CFStringGetTypeID()) { 119 uuid = CFUUIDCreateFromString(kCFAllocatorDefault, fsuuid); 120 121 if(uuid == NULL) { 122 contextprintf(context, kBLLogLevelVerbose, "Bad Volume UUID\n"); 123 } 124 } 125 126 if(uuid) { 127 CFStringRef lastBSDName = CFDictionaryGetValue(dict, CFSTR("BLLastBSDName")); 128 129 // use this as a cache. eventually we may want to scan DiskArb's list of volumes 130 if(lastBSDName && CFGetTypeID(lastBSDName) == CFStringGetTypeID()) { 131 CFUUIDRef dauuid = NULL; 132 133 dauuid = copyVolUUIDFromDiskArb(context, lastBSDName); 134 135 if(dauuid && CFEqual(uuid, dauuid)) { 136 // found it! 137 CFStringGetCString(lastBSDName, bsdName, 138 bsdNameLen, kCFStringEncodingUTF8); 139 140 contextprintf(context, kBLLogLevelVerbose, "Found device: %s\n", bsdName); 141 foundDevice = 1; 142 143 CFRelease(dauuid); 144 } 145 } 146 } 147 148 if (!foundDevice) { 149 // not present, let's hope the matching dictionary was unique enough 150 CFDictionaryRef iomatch = CFDictionaryGetValue(dict, CFSTR("IOMatch")); 151 if(iomatch && CFGetTypeID(iomatch) == CFDictionaryGetTypeID()) { 152 153 CFRetain(iomatch); // IOServiceGetMatchingService releases 1 ref 154 service = IOServiceGetMatchingService(kIOMasterPortDefault,iomatch); 155 if(service != IO_OBJECT_NULL) { 156 157 CFStringRef name = NULL; 158 159 if(!IOObjectConformsTo(service,kIOMediaClass)) { 160 contextprintf(context, kBLLogLevelVerbose, "found service but it is not a media object\n"); 161 } else { 162 163 name = IORegistryEntryCreateCFProperty(service, 164 CFSTR(kIOBSDNameKey), 165 kCFAllocatorDefault, 166 0); 167 if(name && CFGetTypeID(name) == CFStringGetTypeID()) { 168 // if we had a volume uuid, validate it! 169 if(uuid) { 170 CFUUIDRef dauuid = NULL; 171 172 dauuid = copyVolUUIDFromDiskArb(context, name); 173 174 if(dauuid && CFEqual(uuid, dauuid)) { 175 foundDevice = 1; 176 CFRelease(dauuid); 177 } else { 178 // we had a UUID, but this disk didn't, or was incorrect 179 foundDevice = 0; 180 } 181 } else { 182 // we don't have a volume UUID, so assume this is enough 183 foundDevice = 1; 184 } 185 186 if(foundDevice) { 187 CFStringGetCString(name, bsdName, bsdNameLen, kCFStringEncodingUTF8); 188 contextprintf(context, kBLLogLevelVerbose, "Found device: %s\n", bsdName); 189 } 190 } 191 192 if(name) CFRelease(name); 193 } 194 195 IOObjectRelease(service); 196 } 197 } 198 } 199 200 if(uuid) { 201 CFRelease(uuid); 202 } 203 204 if(foundDevice) { 205 return 1; 206 } else { 207 return 0; 208 } 209} 210 211static CFUUIDRef copyVolUUIDFromDiskArb(BLContextPtr context, 212 CFStringRef bsdName) 213{ 214 CFUUIDRef dauuid = NULL; 215#if USE_DISKARBITRATION 216 DASessionRef session = NULL; 217 DADiskRef dadisk = NULL; 218 char lastBSDNameCString[MNAMELEN]; 219 220 CFStringGetCString(bsdName, lastBSDNameCString, 221 sizeof(lastBSDNameCString),kCFStringEncodingUTF8); 222 223 session = DASessionCreate(kCFAllocatorDefault); 224 if(session) { 225 dadisk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, 226 lastBSDNameCString); 227 if(dadisk) { 228 CFDictionaryRef descrip = DADiskCopyDescription(dadisk); 229 if(descrip) { 230 dauuid = CFDictionaryGetValue(descrip, kDADiskDescriptionVolumeUUIDKey); 231 232 if(dauuid) 233 CFRetain(dauuid); 234 CFRelease(descrip); 235 } 236 237 CFRelease(dadisk); 238 } 239 240 CFRelease(session); 241 } 242#endif // USE_DISKARBITRATION 243 return dauuid; 244} 245